Value conversions

Conversion to nlohmann::json

The value of (the elements of) a nlohmann::json object can be set as follows:

int myInt = 1;
nlohmann::json j = myInt;  // j.is_numeric( ) -> true

double myDouble = 1.0;
nlohmann::json k;          // k.is_null( ) -> true
k[ 0 ] = myDouble;         // k.is_array( ) && k[ 0 ].is_numeric( ) && j == k[ 0 ] -> true

However, this does not work (yet):

MyClass myObject = ...
nlohmann::json j = myObject;            // compile error

because the JSON representation of MyClass objects is not known.

At first, one could think that the nlohmann::json class has constructors taking as an argument a basic type such as double, int, bool, std::string, std::vector or std::map. However, this is not true. When writing:

std::string myString = "hi";
nlohmann::json j = myString;

the following is happening behind the scenes:

...
nlohmann::json j;
std::to_json( j, "hi" );
return j;

Indeed, to_json functions are defined for many frequently used types in the file "json/src/json.hpp". These functions are always defined in the namespace of the type that is on the right-hand side of the assignment. We can create to_json functions for our custom classes:

dog.h
namespace animals
{
    class Dog
    {
    public:
        std::string name;
        double weight;

        Dog( const std::string& name, const double weight ) :
          name( name ), weight( weight ) { }
    };
}
jsonInterface.h
#include "json/src/json.hpp"
#include "dog.h"

namespace animals
{
    void to_json( nlohmann::json& j, const Dog& dog )
    {
        j[ "name" ] = dog.name;      // std::to_json( nlohmann::json&, const string& ) will be called
        j[ "weight" ] = dog.weight;  // to_json( nlohmann::json&, const double& ) will be called
    }
}
jsonInterface.cpp
#include "jsonInterface.h"

void printDog( )
{
    nlohmann::json jsonDog = animals::Dog( "Senda", 20 );
    std::cout << jsonDog << std::endl;                      // {"name":"Senda","weight":20}
}

Conversion from nlohmann::json

The value of the elements of a nlohmann::json object can be accessed as follows:

nlohmann::json j = { "no", "yes" };
j[ 0 ];                                                    // "no"
j.at( 1 );                                                 // "yes"

nlohmann::json k = { { "half", 0.5 }, { "twice", 2.0 } };
k[ "half" ];                                               // 0.5
k.at( "twice" );                                           // 2.0

However, as discussed in Value types, the returned values are not numbers or strings, but nlohmann::json objects. Thus:

std::string str = j[ 0 ] + ", thanks";          // compile error

std::string no = j[ 0 ];                        // "no"
std::string str = no + ", thanks";              // "no, thanks"

The implicit conversion is done by overloading the = operator. This means that the following won’t work:

std::string str = std::string( j[ 0 ] ) + ", thanks";     // compile error

However, an explicit conversion is always possible by calling the templated get method:

std::string str = j[ 0 ].get< std::string >( ) + ", thanks";     // "no, thanks"

What is happening behind the scenes, either when an implicit conversion takes place or the get method is called, is:

...
std::string str;                // str -> ""
std::from_json( j, str );       // str -> "no"
return str;

The from_json functions must be defined in the namespace of the type we are converting to. Many are pre-defined for frequently-used types. But, for our Dog class, this is not possible yet:

nlohmann::json jsonDog = { { "name", "Senda" }, { "weight", 20 } };
Dog dog = jsonDog;                                                   // compile error

We have to define its from_json function:

jsonInterface.h
#include "json/src/json.hpp"
#include "dog.h"

namespace animals
{
    ...

    void from_json( const nlohmann::json& j, Dog& dog )
    {
        dog = Dog( j.at( "name" ), j.at( "weight" ) );
    }
}

However, we are still getting a compile error, because this is happening behind the scenes:

...
animals::Dog dog;                  // compile error: Dog is not default-constructible!
animals::from_json( j, dog );
return dog;

Thus, we need to define a default constructor for the class Dog. We can do this by providing default values for all the arguments in the constructor:

dog.h
namespace animals
{
    class Dog
    {
    public:
        std::string name;
        double weight;

        Dog( const std::string& name = "", const double weight = 0.0 ) :
            name( name ), weight( weight ) { }
    };
}

Now, we can do:

nlohmann::json jsonDog = { { "name", "Senda" }, { "weight", 20 } };
Dog dog = jsonDog;                                                   // fine