All Projects → jorgen → json_struct

jorgen / json_struct

Licence: other
json_struct is a single header only C++ library for parsing JSON directly to C++ structs and vice versa

Programming Languages

C++
36643 projects - #6 most used programming language

Projects that are alternatives of or similar to json struct

Deku
Declarative binary reading and writing: bit-level, symmetric, serialization/deserialization
Stars: ✭ 136 (-51.25%)
Mutual labels:  serialization, parse, deserialization
Aspjson
A fast classic ASP JSON parser and encoder for easy JSON manipulation to work with the new JavaScript MV* libraries and frameworks.
Stars: ✭ 165 (-40.86%)
Mutual labels:  serialization, deserialization
Orjson
Fast, correct Python JSON library supporting dataclasses, datetimes, and numpy
Stars: ✭ 2,595 (+830.11%)
Mutual labels:  serialization, deserialization
Dart Json Mapper
Serialize / Deserialize Dart Objects to / from JSON
Stars: ✭ 206 (-26.16%)
Mutual labels:  serialization, deserialization
bytes
Work with bytes and implement network protocols
Stars: ✭ 77 (-72.4%)
Mutual labels:  serialization, deserialization
Noproto
Flexible, Fast & Compact Serialization with RPC
Stars: ✭ 138 (-50.54%)
Mutual labels:  serialization, deserialization
har-rs
A HTTP Archive format (HAR) serialization & deserialization library, written in Rust.
Stars: ✭ 25 (-91.04%)
Mutual labels:  serialization, deserialization
Borer
Efficient CBOR and JSON (de)serialization in Scala
Stars: ✭ 131 (-53.05%)
Mutual labels:  serialization, deserialization
Schematics
Project documentation: https://schematics.readthedocs.io/en/latest/
Stars: ✭ 2,461 (+782.08%)
Mutual labels:  serialization, deserialization
Jsonapi Rb
Efficiently produce and consume JSON API documents.
Stars: ✭ 219 (-21.51%)
Mutual labels:  serialization, deserialization
Jsonapi Rails
Rails gem for fast jsonapi-compliant APIs.
Stars: ✭ 242 (-13.26%)
Mutual labels:  serialization, deserialization
sqlathanor
Serialization / De-serialization support for the SQLAlchemy Declarative ORM
Stars: ✭ 105 (-62.37%)
Mutual labels:  serialization, deserialization
Flatsharp
Fast, idiomatic C# implementation of Flatbuffers
Stars: ✭ 133 (-52.33%)
Mutual labels:  serialization, deserialization
Daw json link
Static JSON parsing in C++
Stars: ✭ 146 (-47.67%)
Mutual labels:  serialization, parse
Pyjson tricks
Extra features for Python's JSON: comments, order, numpy, pandas, datetimes, and many more! Simple but customizable.
Stars: ✭ 131 (-53.05%)
Mutual labels:  serialization, deserialization
Marshmallow Jsonapi
JSON API 1.0 (https://jsonapi.org/) formatting with marshmallow
Stars: ✭ 203 (-27.24%)
Mutual labels:  serialization, deserialization
Awesome Python Models
A curated list of awesome Python libraries, which implement models, schemas, serializers/deserializers, ODM's/ORM's, Active Records or similar patterns.
Stars: ✭ 124 (-55.56%)
Mutual labels:  serialization, deserialization
Yaxlib
Yet Another XML Serialization Library for the .NET Framework and .NET Core
Stars: ✭ 124 (-55.56%)
Mutual labels:  serialization, deserialization
Mashumaro
Fast and well tested serialization framework on top of dataclasses
Stars: ✭ 208 (-25.45%)
Mutual labels:  serialization, deserialization
avro-serde-php
Avro Serialisation/Deserialisation (SerDe) library for PHP 7.3+ & 8.0 with a Symfony Serializer integration
Stars: ✭ 43 (-84.59%)
Mutual labels:  serialization, deserialization

Structurize your JSON

Build status

json_struct is a single header only library that parses JSON to C++ structs/classes and serializing structs/classes to JSON.

It is intended to be used by copying the json_struct.h file from the include folder into the include path for the project. It is only the json_struct.h file that is needed to serialize and deserialize json from structures.

It is dependent on some C++11 features and is tested on newer versions of gcc and clang. It is also tested on VS 2015 and newer.

Structs

json_struct can parse JSON and automatically populate structures with content by adding some metadata to the C++ structs.

{
    "One" : 1,
    "Two" : "two",
    "Three" : 3.333
}

can be parsed into a structure defined like this:

struct JsonObject
{
    int One;
    std::string Two;
    double Three;

    JS_OBJ(One, Two, Three);
};

or

struct JsonObject
{
    int One;
    std::string Two;
    double Three;
};
JS_OBJ_EXT(JsonObject, One, Two, Three);

Populating the struct would look like this:

JS::ParseContext context(json_data);
JsonObject obj;
context.parseTo(obj);

Serializing the struct to json could be done like this:

std::string pretty_json = JS::serializeStruct(obj);
// or
std::string compact_json = JS::serializeStruct(obj, JS::SerializerOptions(JS::SerializerOptions::Compact));

Maps

Sometimes the structure of the JSON is dependent on some value in the JSON. Say there is some input JSON that describes a transportation vehicle. It can looks something like this

{
  "type" : "car",
  "wheels" : 4,
  "electric" : true,
...
}

or it could look like this:

{
  "type" : "sailboat",
  "sail_area_m2" : 106.5,
  "swimming_platform": true,
...
}

This doesn't fit well with the static nature of json_struct. However, it is possible to parse the JSON into a map structure, query some child members for data and then dispatch the conversion into an appropriate type.

void handle_data(const char *data, size_t size)
{
  JS::Map map;
  JS::ParseContext parseContext(data, size, map);
  if (parseContext.error != JS::Error::NoError)
  {
    fprintf(stderr, "Failed to parse Json:\n%s\n", parseContext.makeErrorString().c_str());
    return;
  }
  VehicleType vehicleType = map.castTo<VehicleType>("type", parseContext);
  if (parseContext.error != JS::Error::NoError)
  {
    fprintf(stderr, "Failed to extract type:\n%s\n", parseContext.makeErrorString().c_str());
    return;
  }
  switch (vehicleType)
  {
  case VehicleType::car:
  {
    Car car = map.castTo<Car>(parseContext);
    if (parseContext.error != JS::Error::NoError)
    {
      //error handling 
    }
    handle_car(car);
    break;
  }
  case VehicleType::sailboat:
    Sailboat sailboat;
    map.castToType(parseContext, sailboat);
    if (parseContext.error != JS::Error::NoError)
    {
      //error handling 
    }
    handle_sailboat(sailboat);
    break;
  }
}

Here we parse the JSON into a JS::Map. This map lets us query if the map contains a member and it enables us to convert that member into a type. In the example we convert it to the VehicleType:

  VehicleType vehicleType = map.castTo<VehicleType>("type", parseContext);

Then we can inspect the value of the type child and cast the entire object into the desired type. The cast functions have two signatures: castTo and castToValue. castTo returnes the value type, however, if the object has already been allocated and just needs to populated then castToType. castToType has the added bonus of not needing to specify the template type since this is deduced by the parameter. Casting the whole object has the same semantics it only misses the "name" parameter:

    Car car = map.castTo<Car>(parseContext);
    // or
    Sailboat sailboat;
    map.castToType(parseContext, sailboat);

Demystifying the Macros

The JS_OBJ macro adds a static meta object to the struct/class. It does not affect the semantics or size of the struct/class. It automatically applies another macro to the member arguments, getting the name and member pointer. There are other macros that are more verbose, but that gives more flexibility.

The JS_OBJECT macro requires that all the members are passed in a JS_MEMBER macro. An example of these macros being applied would look like this:

struct JsonObject
{
    int One;
    std::string Two;
    double Three;

    JS_OBJECT(JS_MEMBER(One)
            , JS_MEMBER(Two)
            , JS_MEMBER(Three));
};

This doesn't add any value, but say you want to have a different JSON key for a member than the name, or maybe you want to add some alias keys, then this could be done like this:

struct JsonObject
{
    int One;
    std::string Two;
    double Three;

    JS_OBJECT(JS_MEMBER(One)
            , JS_MEMBER_WITH_NAME(Two, "TheTwo")
            , JS_MEMBER_ALIASES(Three, "TheThree", "the_three"));
};

The difference between the _WITH_NAME and _ALIASES macros is that the _WITH_NAME macro ignores the member name and uses the supplied name, while the aliases adds a list of names to be checked after the name of the member is checked.

Its not possible to use the JS_MEMBER macros with the JS_OBJ macro, since then it tries to apply the JS_MEMBER macro twice on member.

TypeHandler

For objects the meta information is generated with JS_OBJ and JS_OBJECT macros, but there might be types that doesn't fit the meta information interface, ie they are not JSON Object types. Then its possible to define how these specific classes are serialized and deserialized with the TypeHandler interface.

When the JS::ParseContext tries to parse a type it will look for a template specialisation of the type:

namespace JS {
    template<typename T>
    struct TypeHandler;
}

There are a number of predefined template specialisations for types such as:

  • std::string
  • double
  • float
  • uint8_t
  • int16_t
  • uint16_t
  • int32_t
  • uint32_t
  • int64_t
  • uint64_t
  • std::unique_ptr
  • bool
  • std::vector
  • [T]

Its not often necessary, but when you need to define your own serialization and deserialization it's done like this:

namespace JS {
template<>
struct TypeHandler<uint32_t>
{
public:
    static inline Error to(uint32_t &to_type, ParseContext &context)
    {
        char *pointer;
        unsigned long value = strtoul(context.token.value.data, &pointer, 10);
        to_type = static_cast<unsigned int>(value);
        if (context.token.value.data == pointer)
            return Error::FailedToParseInt;
        return Error::NoError;
    }

    static void from(const uint32_t &from_type, Token &token, Serializer &serializer)
    {
        std::string buf = std::to_string(from_type);
        token.value_type = Type::Number;
        token.value.data = buf.data();
        token.value.size = buf.size();
        serializer.write(token);
    }
};
}

This gives you complete control of serialization deserialization of a type and it can unfold to a JSON object or array if needed.

For more information checkout the examples at: https://github.com/jorgen/json_struct/tree/master/examples

and have a look at the more complete unit tests at: https://github.com/jorgen/json_struct/tree/master/tests

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].