I'm writing a Kotlin program (though there's nothing Kotlin-specific here, so Java-based answers are also welcome) that needs to communicate using a CBOR-based protocol. That protocol uses integers as field names to save space, and changing the protocol is not an option.
A simple example:
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)}")
}
Which outputs: Bytes as Base64: v2NzdHJlSGVsbG//
To see what's coming out the other end, I use a Python script:
from cbor2 import loads
from base64 import b64decode
s = "v2NzdHJlSGVsbG//"
print(loads(b64decode(s)))
Which prints {'str': 'Hello'}
What I'd like instead is {1: 'Hello'}
All of the answers I've found online about integer keys seem focused on types like Map<Integer,String>
instead of POJOs, and indeed I can get the desired result with:
val testMap = mapOf(1 to "Hello")
val mapper = CBORMapper()
val bytes = mapper.writeValueAsBytes(testMap)
println("Bytes as Base64: ${Base64.getEncoder().encodeToString(bytes)}")
But marshalling everything in and out of a Map object defeats the point of using a data binding library like Jackson.
In an ideal world, I'd be able to do this with a simple annotation, e.g.:
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.
The obvious brute force solution is writing my own custom serializer for the class using JsonGenerator.writeFieldId()
to write integer field labels, but I feel like that's going to balloon in complexity very quickly, especially since this protocol will involve a few dozen classes and, as I understand it, I would need to write a separate custom serializer for each one.
Is there an easier way that I'm missing?