#java #kotlin #jackson #cbor
Вопрос:
Я пишу программу Kotlin (хотя здесь нет ничего специфичного для Kotlin, поэтому ответы на основе Java также приветствуются), которая должна взаимодействовать с использованием протокола на основе CBOR. Этот протокол использует целые числа в качестве имен полей для экономии места, и изменение протокола не является опцией.
Простой пример:
package com.example.cbortest
import com.fasterxml.jackson.dataformat.cbor.databind.CBORMapper
import java.util.*
class CBORTest(
val str: String
)
fun main() {
val testObj = CBORTest("Hello")
val mapper = CBORMapper()
val bytes = mapper.writeValueAsBytes(testObj)
println("Bytes as Base64: ${Base64.getEncoder().encodeToString(bytes)}")
}
Какие результаты: Bytes as Base64: v2NzdHJlSGVsbG//
Чтобы увидеть, что выходит с другого конца, я использую скрипт на Python:
from cbor2 import loads
from base64 import b64decode
s = "v2NzdHJlSGVsbG//"
print(loads(b64decode(s)))
Какие отпечатки {'str': 'Hello'}
Чего бы я хотел вместо этого, так это {1: 'Hello'}
Все ответы, которые я нашел в Интернете о целочисленных ключах, кажутся сосредоточенными на таких типах, как Map<Integer,String>
вместо POJOs, и действительно, я могу получить желаемый результат с помощью:
val testMap = mapOf(1 to "Hello")
val mapper = CBORMapper()
val bytes = mapper.writeValueAsBytes(testMap)
println("Bytes as Base64: ${Base64.getEncoder().encodeToString(bytes)}")
Но сортировка всего, что входит и выходит из объекта карты, лишает смысла использовать библиотеку привязки данных, такую как Джексон.
В идеальном мире я мог бы сделать это с помощью простой аннотации, например:
class CBORTest(
@JsonProperty(1)
val str: String
)
But the JsonProperty annotation only accepts string values, and @JsonProperty(index=1)
only affects the ordering of the properties in the generated map.
I also tried:
class IntKeySerializer: JsonSerializer<String>() {
override fun serialize(value: String, gen: JsonGenerator, serializers: SerializerProvider) {
gen.writeFieldId(1)
}
}
class CBORTest(
@JsonSerialize(keyUsing=IntKeySerializer::class)
val str: String
)
but @JsonSerialize(keyUsing=...)
seems specific to map fields.
Очевидным решением грубой силы является написание моего собственного пользовательского сериализатора для класса, использующего JsonGenerator.writeFieldId()
для записи меток целочисленных полей, но я чувствую, что это очень быстро усложнится, тем более что этот протокол будет включать несколько десятков классов, и, как я понимаю, мне нужно будет написать отдельный пользовательский сериализатор для каждого из них.
Есть ли более простой способ, которого мне не хватает?