将固定大小的 Map 序列化为 CBOR

Serialize fixed size Map to CBOR

我有以下 JSON:

[
  {
    2: {
      "c": true
    }
  },
  {
    3: {
      "p": 10
    }
  }
]

我想转换成 CBOR 格式。 根据 cbor.me 我有以下输出:

82A102A16163F5A103A161700A

但是,当使用 Jackson Binary CBOR Serializer 时,我有以下输出:

82BF02BF6163F5FFFFBF03BF61700AFFFF

这没有错,但没有优化...我在实际情况下添加了额外的 4 个不必要的字节。

然后我尝试手动序列化 JSON 但结果相同:

@Override
public void serialize(Request value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonProcessingException {
    jgen.writeStartArray(value.getDataList().size());
    for (Data data : value.getDataList()) {
        jgen.writeStartObject(new Map[1]);
        jgen.writeFieldId(data.getItem());
        jgen.writeStartObject();
        if (data.getObject().getC() != null) {
            jgen.writeBooleanField("c", data.getObject().getC());
        }
        if (data.getObject().getP() != null) {
            jgen.writeNumberField("p", data.getObject().getP());
        }
        jgen.writeEndObject();
        jgen.writeEndObject();
    }
    jgen.writeEndArray();
}

这是 Jackson 二进制格式库的错误还是我缺少 ObjectMapper 的一些配置属性?

编辑:这似乎是一个已知问题:https://github.com/FasterXML/jackson-dataformats-binary/issues/3

通过使用版本 2.9.4,以下方法在 CBORGenerator class 中可用:public final void writeStartObject(int elementsToWrite)

@Override
public void serialize(Request value, JsonGenerator jgen, SerializerProvider provider)
        throws IOException, JsonProcessingException {
    jgen.writeStartArray(value.getDataList().size());
    for (Data data : value.getDataList()) {
        ((CBORGenerator) jgen).writeStartObject(1);
        jgen.writeFieldId(data.getItem());
        ((CBORGenerator) jgen).writeStartObject(1);
        if (data.getObject().getC() != null) {
            jgen.writeBooleanField("c", data.getObject().getC());
        }
        if (data.getObject().getP() != null) {
            jgen.writeNumberField("p", data.getObject().getP());
        }
        jgen.writeEndObject();
        jgen.writeEndObject();
    }
    jgen.writeEndArray();
}

我有以下输出:

82A102A16163F5A103A161700A

您已经得到了使用更新或更好的编码器的答案。但是对于后来来到这里的其他人...

问题是 OP 的编码器使用了不定长度的映射,然后 "BREAK" 基元跳出并转到下一个项目。

与break原语版本比较:

82             # array(2)
   BF          # map(*)
      02       # unsigned(2)
      BF       # map(*)
         61    # text(1)
            63 # "c"
         F5    # primitive(21)
         FF    # primitive(*)
      FF       # primitive(*)
   BF          # map(*)
      03       # unsigned(3)
      BF       # map(*)
         61    # text(1)
            70 # "p"
         0A    # unsigned(10)
         FF    # primitive(*)
      FF       # primitive(*)

没有它们的版本:

82             # array(2)
   A1          # map(1)
      02       # unsigned(2)
      A1       # map(1)
         61    # text(1)
            63 # "c"
         F5    # primitive(21)
   A1          # map(1)
      03       # unsigned(3)
      A1       # map(1)
         61    # text(1)
            70 # "p"
         0A    # unsigned(10)

你看到地图 (*) 和地图 (1) 了吗?

通过使用特定长度的映射而不是不确定的长度,生成的 CBOR 可以使用 "One map coming, here it is" 而不是 "IDK! Maps coming! Here is one! Now Stop!"

在第二个示例中,仍然有一个原语,但它不是 BREAK 命令。 0xF5 实际上意味着 "true"。从 0xF5 (11110101) 中取出前三位(CBOR Major Type),您就得到了已建立的 CBOR "true" (0x00010101) 的十进制 21。

此外,将值 2 指定为其中包含 "c"="true" 的地图的名称是完全有效的。但请注意,如果您担心将值用作名称时转换为 JSON 将会出现问题。

这是不应该使用无限长度的较差编码器的问题 maps/breaks。有时会使用它们,但只能在 "streaming" 模式下使用,这对于给定的示例来说不太可能。如果您预先拥有所有项目并进行编码,则不需要使用不定式。如果您有一定数量的地图,但不确定有多少,并且想开始对您拥有的地图进行编码,那么您将需要无限长度的地图或字符串。