Search code examples
c++jsonc++11nlohmann-json

creating nested json object in c++ using nlohmann json


I am working with https://github.com/nlohmann/json and it works well. However I am finding difficulties to create the following json outout

 {
    "Id": 1,
    "Child": [
        {
            "Id": 2
        },
        {
            "Id": 3,
            "Child": [
                {
                  "Id" : 5
                },
                {
                  "Id" : 6
                }
            ]
        },
        {
            "Id": 4
        }
    ]
}

Every node must have an id and an array ("Child" element). Any child can recursively continue to have Id or Child. The json above is just an example. What I want is to create a chain between father and children node using nlohmann json.

Numbers 1, 2, 3, .... are picked up randomly. We don't care for now about those values.

Any idea how to create it?

Code so far

#include <iostream>
#include <string>
#include <vector>
#include "json.hpp"

using json = nlohmann::json;


struct json_node_t {

    int id;
    std::vector<json_node_t> child;
};


int main( int argc, char** argv) {

    json j;

    for( int i = 0; i < 3; i++) {

        json_node_t n;

        n.id = i;
        j["id"] = i;

        if ( i < 2 ) {

            j["child"].push_back(n);


        }


    }


    return 0;

}

Solution

  • In order to serialize your own type, you need to implement a to_json function for that type.

    #include <iostream>
    #include <string>
    #include <vector>
    #include "json.hpp"
    
    using namespace std;
    using json = nlohmann::json;
    
    struct json_node_t {
        int id;
        std::vector<json_node_t> child;
    };
    
    void to_json(json& j, const json_node_t& node) {
        j = {{"ID", node.id}};
        if (!node.child.empty())
            j.push_back({"children", node.child});
    }
    
    int main() {
        json_node_t node = {1, {{2, {}}, {3, {{5, {}}, {6, {}}}}, {4, {}}}};
        json j = node;
    
        cout << j.dump(2) << endl;
        return 0;
    }
    

    Output:

    {
      "ID": 1,
      "children": [
        {
          "ID": 2
        },
        {
          "ID": 3,
          "children": [
            {
              "ID": 5
            },
            {
              "ID": 6
            }
          ]
        },
        {
          "ID": 4
        }
      ]
    }
    

    A couple of more ways to initialize json_node_t (all producing the same tree and the same output):

    struct json_node_t {
        int id;
        std::vector<json_node_t> child;
        json_node_t(int node_id, initializer_list<json_node_t> node_children = initializer_list<json_node_t>());
        json_node_t& add(const json_node_t& node);
        json_node_t& add(const initializer_list<json_node_t>& nodes);
    };
    
    json_node_t::json_node_t(int node_id, initializer_list<json_node_t> node_children) : id(node_id), child(node_children) {
    }
    
    json_node_t& json_node_t::add(const json_node_t& node) {
        child.push_back(node);
        return child.back();
    }
    
    json_node_t& json_node_t::add(const initializer_list<json_node_t>& nodes) {
        child.insert(child.end(), nodes);
        return child.back();
    }
    
    int main() {
        json_node_t node_a = {1, {{2, {}}, {3, {{5, {}}, {6, {}}}}, {4, {}}}};
    
        json_node_t node_b = {1, {2, {3, {5, 6}}, 4}};
    
        json_node_t node_c(1);
        node_c.add(2);
        node_c.add(3).add({5, 6});
        node_c.add(4);
    
        cout << json(node_a).dump(2) << endl << endl;
        cout << json(node_b).dump(2) << endl << endl;
        cout << json(node_c).dump(2) << endl;
        return 0;
    }