Целочисленные ключи при сериализации POJO в CBOR с помощью Jackson

#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() для записи меток целочисленных полей, но я чувствую, что это очень быстро усложнится, тем более что этот протокол будет включать несколько десятков классов, и, как я понимаю, мне нужно будет написать отдельный пользовательский сериализатор для каждого из них.

Есть ли более простой способ, которого мне не хватает?