Created by: Matheus-Garbelini
Hello @bblanchon , Even tough messagepack support is coming with a new API in the next version of ArduinoJson, I think this PR can be useful in some sort with messagepack deserializer and serializer. Even knowing this PR may not go forward, it can be a nice tool to changes and new possibilities to a oficial future support.
I added an example in called MessagePack to test it on an Arduino. The changes add the following new methods to serialize or deserialize messagepack binary vectors:
MessagePack Serializer
char JsonText[64] = {0};
DynamicJsonBuffer jsonBuffer;
JsonObject &exampleSerializer = jsonBuffer.createObject();
exampleSerializer["T"] = -2.3;
exampleSerializer["C"] = -128000;
// JsonText array is filled with a message pack message (use size variable to transmit only the message size)
uint8_t size = exampleSerializer.binaryPrintTo(JsonText, sizeof(JsonText));
MessagePack Deserializer
uint8_t testVector[] = {133, 161, 84, 203, 192, 2, 102, 102, 96, 0, 0, 0, 161, 67, 210, 255, 254, 12, 0, 161, 68, 146, 194, 195, 161, 69, 132, 165, 68, 97, 116, 97, 49, 2, 165, 68, 97, 116, 97, 50, 205, 4, 5, 165, 68, 97, 116, 97, 51, 210, 255, 254, 255, 255, 165, 68, 97, 116, 97, 52, 166, 83, 116, 114, 105, 110, 103, 161, 70, 146, 165, 99, 104, 97, 114, 49, 165, 99, 104, 97, 114, 50};
DynamicJsonBuffer jsonBuffer;
// Here the messagepack array is parsed
JsonObject &exampleDeserializer = jsonBuffer.parseBinaryObject(testVector);
// Further convertions is done in when JsonVariant is called.
Serial.println("T: " + String((float)exampleDeserializer["T"]));
Serial.println("C: " + String((int32_t)exampleDeserializer["C"]));
Serial.println("D[0]: " + String(((bool)exampleDeserializer["D"][0] ? "true" : "false")));
Serial.println("D[1]: " + String(((bool)exampleDeserializer["D"][1] ? "true" : "false")));
Serial.println("E[\"Data1\"]: " + String((int)exampleDeserializer["E"]["Data1"]));
Serial.println("E[\"Data2\"]: " + String((int)exampleDeserializer["E"]["Data2"]));
Serial.println("E[\"Data3\"]: " + String((int32_t)exampleDeserializer["E"]["Data3"]));
Serial.println("E[\"Data4\"]: " + String((const char *)exampleDeserializer["E"]["Data4"]));
Serial.println("F[\"char1\"]: " + String((const char *)exampleDeserializer["F"][0] ));
Serial.println("F[\"char2\"]: " + String((const char *)exampleDeserializer["F"][1] ));
How does the new message support works?
Arduino Json is an incredible well written and complex library, thus I tried to make lowest changes as possibles to the API. Most things were just added, nothing was deleted and didn't use external libraries.
Almost everything of the messagepack specs was covered, but the extended types and timestamp types was not included in this implementation.
Serializer
Serializer was the most easy.
- Added new methods in JsonWritter.hpp dedicated to write the json types (int, float, string) according to messagepack specs.
- JsonBinarySerializerImpl.hpp was added as as interface to call specific methods within JsonWritter according to the variant type (if it was integer, float, boolean or string).
- As i didn't want to create a new Serialization class at first, JsonSerializer.hpp was used to include the function calls to JsonBinarySerializerImpl.hpp
- To wrap everything to the user public interface, binaryPrintTo was added in JsonPrintable.hpp calling the binarySerialization function inside JsonSerializer.hpp. (Maybe it would be a good idea if i had created a different JsonSerializer class like "JsonBinarySerializer" so it would make the code more clean
Deserializer
This was almost a 7-head monster to understand and have a more or less clean path to where to implement a new Deserializer, but everything was awesome after using the debuger.
-
parseFloat.hpp and parseInteger.hpp were the first codes to be worked, they had to be clean, fast and support not just arduino but every platform. I had to put extra effort in casting there. As atmega328 double size is 4 bytes and messagepack specs for double is 8 bytes i had to include a aditional double to float convertion code in Polyfills/convertDouble.hpp (new file)
-
This time i did it right :). A new class called JsonBinaryParser was created to include the messagepack parser (JsonBinaryParserImpl.hpp and JsonBinaryParser.hpp). The strategy in this class was to identify the messagepack frames (maps, arrays, strings, integers, floats, boolean) and append them in the variant (stored as a string). Each time the function parseAnything was called, a flag was set in the new created jsonvariant (_parserType), so it can know later if the values needs to be binary converted instead of converted from a json text.
-
JsonBufferBase.hpp was added with new methods so the new parseBinaryArray class could be used via public interface with the user after a static or dynamic buffer was created.
-
Json variant was added a new private property called _parserType wich is 0 for JsonParser and 1 for binaryJsonParser (0 by default) so each time the user requests a variable, the jsonvariant checks which parser was used so it can use the corrects parseFloat, parseInteger and parseBoolean.
It's the first time modifyng a huge library as this so somethings may be not well optimized nor the best approach to include this kind of support. Thanks for your attention.