Skip to content

Trisfald/jsontype

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jsontype

Jsontype is a tiny library for managing json in a structured way. It's built on top of rapidjson, which is used to handle the json creation and parsing.

Prerequisites

Installation

Add the jsontype directory in your includes. The library is header only!
Tests use the google test framework. Examples are in the src directory and don't use any extra dependency.

How to use

Suppose we want to map a json with the following structure into a type:

{
	"name": "Paul",
	"age": 20,
	"contact": {
		"address": "74 Green St",
		"phone": "564565132"
	}
}

We can do it in this way:

// Create tags either with a macro or manually
JSONTYPE_MAKE_TAG(name);
JSONTYPE_MAKE_TAG(age);
JSONTYPE_MAKE_TAG(contact);
JSONTYPE_MAKE_TAG(address);
struct phone_tag : Tag<phone_tag> { static constexpr const auto name() { return "phone"; } };

// Define the json structure
using Person = Root<Value_field<name_tag, std::string>,
		Value_field<age_tag, unsigned>,
		Object<contact_tag,
				Value_field<address_tag, std::string>,
				Value_field<phone_tag, std::string>>>;	

Now we can create instances of this json by simply creating an object. All values are initially default constructed, in fact printing the object's string representation gives us:

{"name":"","age":0,"contact":{"address":"","phone":""}}

Parsing and move initialization

It's possible to initialize a root object with an already existent rapidjson document or with a string. The library will check if the given json has a structure compatible with the one embedded in the object's type: it can have additional elements but all the specified ones must be present and of the right kind. When a string is passed in to the constructor, parsing will be done automatically.

Finding and manipulating nodes

A jsontype object can be navigated by using the get() member function or the operator[], passing a tag instance. A value field's current value can be read or changed via member functions and operators.

// Set values
person[name_tag{}] = "Mario";
person[contact_tag{}][phone_tag{}] = "435425245";
auto contact = person[contact_tag{}]; // it might be useful to use an handle to the contact object
contact[address_tag{}] = "some street";

// Read values
const auto name = person[name_tag{}].get();
std::string phone = person[contact_tag{}][phone_tag{}]; // don't use auto here!

Keys

Keys are used to identify and access nodes; they can be added together or bundled in a new type. Here are presented some valid ways to retrieve the address from our json:

using contact_key = Key<contact_tag>;
using address_leaf_key = Key<address_tag>;
using address_key = Key<contact_tag, address_tag>;
using composite_key = Key<contact_key, address_leaf_key>;

std::string address = person[contact_key{} + address_leaf_key{}];
address = person[contact_key{} + address_tag{}];
address = person[address_key{}];
address = person[composite_key{}];

Resolver

Sometimes it may be useful to do associate different actions to different kinds of json objects. A resolver does just that; an actions is represented by a callable object and it's linked to a particular json structure with a key.
In the following example we have two json: one models a car and the other a bike. We then setup a resolver to correctly compute the tires' cost.

// Create the tags
JSONTYPE_MAKE_TAG(vehicle);
JSONTYPE_MAKE_TAG(car);
JSONTYPE_MAKE_TAG(bike);
// Create the keys
using key_car = Key<vehicle_tag, car_tag>;
using key_bike = Key<vehicle_tag, bike_tag>;
// Some example inputs
constexpr auto car_json = "{\"vehicle\":{\"car\":{}}}";
constexpr auto bike_json = "{\"vehicle\":{\"bike\":{}}}";

Resolver<int(*)(int)> resolver; // could also be parametrized with a std::function or with a functor

resolver.add(key_car{}, [](int price) { return price * 4; });  // cars have 4 tyres
resolver.add(key_bike{}, [](int price) { return price * 2; }); // and bikes 2

auto total = resolver.scan(car_json, 10); // total = 40
total = resolver.scan(bike_json, 10); // total = 20