nlohmann / json

JSON for Modern C++
https://json.nlohmann.me
MIT License
43.13k stars 6.73k forks source link

Serialization for CBOR #726

Closed cblauvelt closed 7 years ago

cblauvelt commented 7 years ago

First, thanks for the great library. It really is easy to work with.

I would like to see a to_cbor and from_cbor implemented as serialization options. My specific use case is that for JSON, to make it more readable, I'm translating my enums through strings. I'd much rather use the integer equivalent for enums when I'm using CBOR to reduce the amount of data sent over the wire.

pboettch commented 7 years ago

Wouldn't it be easier to specialize, somehow, the printing of a JSON object (and the parsing probably as well) and when handling your field you are stringifying the enum-value (or inverse). In that case the internal storage would be the enum-value (integer probably).

EDIT: maybe not easier but more versatile and useful for future uses and users.

nlohmann commented 7 years ago

I think this would mean a lot of work for a rather specific usecase. For an intermediate solution, you could write an additional method just like @pboettch proposed.

cblauvelt commented 7 years ago

I'm sorry @pboettch but I'm not sure what you're suggesting. To make this less abstract here is an example of what I'm currently doing.

enums.h

 enum class DataType : uint8_t {
        /// InvalidDataType An invalid type set by the default constructor. Do not use.
        InvalidDataType = 0,
        /// InputStatus This type of data can be provided by an I/O system.
        InputStatus = 1,
        /// CoilStatus This type of data can be alterable by an application program
        Coil = 2,
        /// InputRegister This type of data can be provided by an I/O system
        InputRegister = 3,
        /// HoldingRegister This type of data can be alterable by an application program.
        HoldingRegister = 4,
        /// This contains a confirmation that the associated coils/registers were written
        WriteConfirmation = 5,
    };

    std::string to_string(const DataType t);
    void from_string(const std::string s, DataType& t);
    void to_json(json& j, const DataType& t);
    void from_json(const json& j, DataType& t);

enums.cpp

std::string to_string(const DataType t) {
    switch (t) {
    case DataType::InvalidDataType:
        return "InvalidDataType";
    case DataType::InputStatus:
        return "InputStatus";
    case DataType::Coil:
        return "Coil";
    case DataType::InputRegister:
        return "InputRegister";
    case DataType::HoldingRegister:
        return "HoldingRegister";
    case DataType::WriteConfirmation:
        return "WriteConfirmation";
    }

    return "";
}

void from_string(const std::string s, DataType& t) {
    if(s == "InvalidDataType") {
        t = DataType::InvalidDataType;
        return;
    }
    if(s == "Coil") {
        t = DataType::InputStatus;
        return;
    }
    if(s == "Coil") {
        t = DataType::Coil;
        return;
    }
    if(s == "InputRegister") {
        t = DataType::InputRegister;
        return;
    }
    if(s == "HoldingRegister") {
        t = DataType::HoldingRegister;
        return;
    }
    if(s == "WriteConfirmation") {
        t = DataType::WriteConfirmation;
        return;
    }
}

void to_json(json& j, const DataType& t) {
        j = to_string(t);
}

void from_json(const json& j, DataType& t) {
        from_string(j.get<std::string>(), t);
}
pboettch commented 7 years ago

OT: You're not, by any chance, trying to address encapsulate modbus-transfers within a JSON-message using libmodbus ?

pboettch commented 7 years ago

What I suggest is something which probably does not exist yet: Intercepting the print-out of json (via dump or operator<<()) and replacing the to-be-printed value by something else. Following the same idea you had with cbor-en/decoding but doing it when printing/dumping the text-version.

You could also do a simple specialized print functions for your json-instance which prints the string instead of the ID.

If I understood correctly, dumping your JSON is for debug reasons. If so, have you considered a json-schema-validation of your structure?

cblauvelt commented 7 years ago

I'm doing a couple things. I'm parsing configuration files and that includes some enumerated values that make more sense in string form (Client, Server) versus the enumerated value (0,1, What was 1 again?).

The other thing I'm trying to do is encapsulate MODBUS transfers into JSON/CBOR and allow plaintext versions for enumerated values for the purposes of debugging but also use the integer values when using a more concise (see what I did there?) format like CBOR.

Normally I would separate the two but in this instance I'm defining a MODBUS poll in a configuration file and the client needs to know what data it's getting back so I'm using the value in a configuration file, and in the object representation. I could define two enums and keep them the same but that sounds like a bad long term strategy.

What do you think about using Enum2 = Enum1; Could I define different to/from_json methods for each, convert 1 to a string and convert 2 to an integer?

pboettch commented 7 years ago

The bi-directional stringification of enum-values in C++ is (still) not (yet) solved, not even with this library. I don't have any good idea of how to automatically simplify your case.

(Btw. my modbus-remark was because it seems that I'm doing similar things with modbus and JSON and I'm not even using CBOR on message-bus, but I should. My modbus-function-IDs are strings - for the moment the bottle-neck is the modbus-bus, not the message-bus nor the CPU).