我正在编写一个需要使用基于 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()
编写整数字段标签的类编写我自己的自定义序列化程序,但我觉得这将很快变得复杂,特别是因为这个协议将涉及几十个类,并且据我所知,我需要为每个单独编写一个自定义序列化程序。
有没有我想念的更简单的方法?