Serialize fixed size Map to CBOR

荒凉一梦 提交于 2020-03-01 06:43:18

问题


I have the following JSON:

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

That I would like to convert to CBOR format. Accordingly to cbor.me I have the following output:

82A102A16163F5A103A161700A

But, when using Jackson Binary CBOR Serializer, I have the following output:

82BF02BF6163F5FFFFBF03BF61700AFFFF

Which is not wrong, but not optimized... I have an extra 4 unnecessary bytes added to what it can really be.

I've then tried to manually serialize the JSON but same result:

@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();
}

Is this a bug with Jackson Binary format library or am I missing some configuration properties from the ObjectMapper?

EDIT: This seems to be a known issue: https://github.com/FasterXML/jackson-dataformats-binary/issues/3


回答1:


You've already received the answer in using a newer or better encoder. But for anyone else that comes here later...

The issue is OP's encoder was using indefinite length maps, then "BREAK" primitives to break out and go to the next item.

Compare the version with break primitives:

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(*)

To the version without them:

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)

Do you see the map(*) vs map(1)?

By using maps with specific lengths instead of indefinite length, the resulting CBOR can use "One map coming, here it is" instead of "IDK! Maps coming! Here is one! Now Stop!"

In the second example, there is still a primitive, but it's not a BREAK command. The 0xF5 effectively means "true". Take the first three bits (CBOR Major Type) away from 0xF5 (11110101) and you have decimal 21 the established CBOR "true" (0x00010101).

Also, it's entirely valid to assign the value 2 as the name of a map with "c"="true" inside of it. But beware that converting to JSON when using values as names will be problematic if that is something you are concerned about.

This was an issue with a poor encoder that should not have been using indefinite length maps/breaks. There is a time to use those, but only in a "streaming" mode which is unlikely for the example given. If you have all the items up front and encode, using indefinites is not required. If you have some number of maps but aren't sure how many and want to get started on encoding what you do have, that's when you'll want indefinite length maps or strings.




回答2:


By using version 2.9.4 the following method is available in the 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();
}

And I have the following output:

82A102A16163F5A103A161700A



来源:https://stackoverflow.com/questions/48991682/serialize-fixed-size-map-to-cbor

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!