3

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?

thoom
  • 31
  • 2

0 Answers0