2

我正在编写一个需要使用基于 CBOR 的协议进行通信的 Kotlin 程序(尽管这里没有特定于 Kotlin 的内容,因此也欢迎基于 Java 的答案)。该协议使用整数作为字段名称以节省空间,并且更改协议不是一种选择。

一个简单的例子:

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>而不是 POJO,实际上我可以通过以下方式获得所需的结果:

val testMap = mapOf(1 to "Hello")
val mapper = CBORMapper()
val bytes = mapper.writeValueAsBytes(testMap)
println("Bytes as Base64: ${Base64.getEncoder().encodeToString(bytes)}")

但是编组进出 Map 对象的所有内容破坏了使用像 Jackson 这样的数据绑定库的意义。


在一个理想的世界里,我可以用一个简单的注释来做到这一点,例如:

class CBORTest(
    @JsonProperty(1)
    val str: String
)

但是 JsonProperty 注解只接受字符串值,并且@JsonProperty(index=1)只影响生成映射中属性的顺序。

我也试过:

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
)

@JsonSerialize(keyUsing=...)似乎特定于地图字段。

显而易见的蛮力解决方案是为用于JsonGenerator.writeFieldId()编写整数字段标签的类编写我自己的自定义序列化程序,但我觉得这将很快变得复杂,特别是因为这个协议将涉及几十个类,并且据我所知,我需要为每个单独编写一个自定义序列化程序。

有没有我想念的更简单的方法?

4

0 回答 0