Search code examples
jsonboostboost-propertytree

boost property tree json


i have a JSON file:

{
   "level_1" :
   {
      "level_1_1" : [[1, "text", 0, 1, 2],
                     [2, "text", 1, 3, 4],
                     [3, "text", 5, 6, 8],
                     [7, "text", 5, 4, 3]
                    ],
      "level_1_2" : [[....]],
      "level_1_3" : [[....]]
   }
}

I want to get content get_child(level_1.level_1_1) and put it in std::map<int, struct json_data>, where

struct json_data {
   std::string str;
   int num1, num2, num3;
};

How can I do this?


Solution

  • Please don't use Property Tree as if it were a JSON library.

    In boost 1.75 you can use Boost Json:

    Boost Json

    Boost Json allows you to have automatic conversions using value_to<T>. Extend it for your own type with tag_invoke:

    struct json_data {
       std::string str;
       int64_t num1, num2, num3;
    
       //for json input
       friend json_data tag_invoke(json::value_to_tag<json_data>, json::value const& v) {
           auto& arr = v.as_array();
           return {
               value_to<std::string>(arr[1]),
               //std::string(s.data(), s.size()),
               arr[2].as_int64(),
               arr[3].as_int64(),
               arr[4].as_int64()
           };
       }
    };
    

    Now you can "just" read it like:

    auto doc = json::parse(json_text);
    
    using SubLevel = std::vector<json_data>;
    using Level    = std::map<std::string, SubLevel>;
    auto level_1   = json::value_to<Level>(doc.as_object()["level_1"]);
    

    It just works. You can take it a step further and parse the whole file:

    using File = std::map<std::string, Level>;
    auto all   = json::value_to<File>(doc);
    fmt::print("Whole file: {}\n", fmt::join(all, "\n"));
    

    Live Demo

    Live On Compiler Explorer

    #include <boost/json.hpp>
    #include <boost/json/src.hpp> // for header only
    #include <iostream>
    #include <iomanip>
    #include <fmt/ranges.h>
    #include <fmt/ostream.h>
    namespace json = boost::json;
    
    struct json_data {
    std::string str;
    int64_t num1, num2, num3;
    
    //for json input
    friend json_data tag_invoke(json::value_to_tag<json_data>, json::value const& v) {
        auto& arr = v.as_array();
        return {
            value_to<std::string>(arr[1]),
            //std::string(s.data(), s.size()),
            arr[2].as_int64(),
            arr[3].as_int64(),
            arr[4].as_int64()
        };
    }
    
    //for debug output
    friend std::ostream& operator<<(std::ostream& os, json_data const& jd) {
        return os << '{' << std::quoted(jd.str) << ';' << jd.num1 << ';'
                    << jd.num2 << ';' << jd.num3 << '}';
    }
    };
    
    extern std::string const json_text;
    
    int main() {
        auto doc = json::parse(json_text);
    
        using SubLevel = std::vector<json_data>;
        using Level    = std::map<std::string, SubLevel>;
        auto level_1   = json::value_to<Level>(doc.as_object()["level_1"]);
    
        fmt::print("Level 1: {}\n", fmt::join(level_1, "\n\t - "));
    
        using File = std::map<std::string, Level>;
        auto all   = json::value_to<File>(doc);
        fmt::print("Whole file: {}\n", all);
    }
    
    std::string const json_text = R"(
    {
        "level_1": {
            "level_1_1": [
                [1, "text", 0, 1, 2],
                [2, "text", 1, 3, 4],
                [3, "text", 5, 6, 8],
                [7, "text", 5, 4, 3]],
            "level_1_2": [
                [9, "text", 8, 9, 10],
                [10, "text", 9, 11, 12],
                [11, "text", 13, 14, 16],
                [15, "text", 13, 12, 11]],
            "level_1_3": [
                [17, "text", 16, 17, 18],
                [18, "text", 17, 19, 20],
                [19, "text", 21, 22, 24],
                [23, "text", 21, 20, 19]]
        }
    })";
    

    Prints

    Level 1: ("level_1_1", {{"text";0;1;2}, {"text";1;3;4}, {"text";5;6;8}, {"text";5;4;3}})
             - ("level_1_2", {{"text";8;9;10}, {"text";9;11;12}, {"text";13;14;16}, {"text";13;12;
    11}})
             - ("level_1_3", {{"text";16;17;18}, {"text";17;19;20}, {"text";21;22;24}, {"text";21;
    20;19}})
    
    Whole file: ("level_1", {("level_1_1", {{"text";0;1;2}, {"text";1;3;4}, {"text";5;6;8}, {"text
    ";5;4;3}}), ("level_1_2", {{"text";8;9;10}, {"text";9;11;12}, {"text";13;14;16}, {"text";13;12
    ;11}}), ("level_1_3", {{"text";16;17;18}, {"text";17;19;20}, {"text";21;22;24}, {"text";21;20;
    19}})})
    

    Boost Property Tree

    If you must, you can try to get close using Boost Property Tree:

    Live On Coliru

    #define BOOST_BIND_GLOBAL_PLACEHOLDERS 
    #include <boost/property_tree/json_parser.hpp>
    #include <iostream>
    #include <sstream>
    #include <iomanip>
    #include <fmt/ranges.h>
    #include <fmt/ostream.h>
    
    using boost::property_tree::ptree;
    
    struct json_data {
    std::string str;
    int num1, num2, num3;
    
    friend void read_tree(ptree const& pt, std::vector<json_data>& into) {
        auto r = pt.equal_range("");
        for (;r.first != r.second; ++r.first) {
            read_tree(r.first->second, into.emplace_back());
        }
    }
    
    friend void read_tree(ptree const& pt, json_data& into) {
        auto r = pt.equal_range("");
        assert(std::distance(r.first, r.second) == 5);
    
        auto it = r.first;
        into = {
            (++it)->second.get_value<std::string>(),
            //std::string(s.data(), s.size()),
            (++it)->second.get_value<int>(),
            (++it)->second.get_value<int>(),
            (++it)->second.get_value<int>()
        };
    }
    
    //for debug output
    friend std::ostream& operator<<(std::ostream& os, json_data const& jd) {
        return os << '{' << std::quoted(jd.str) << ';' << jd.num1 << ';'
                    << jd.num2 << ';' << jd.num3 << '}';
    }
    };
    
    extern std::string const json_text;
    
    int main() {
        ptree pt;
        {
            std::istringstream iss(json_text);
            read_json(iss, pt);
        }
    
        std::vector<json_data> level_1;
        read_tree(pt.get_child("level_1.level_1_2"), level_1);
        fmt::print("level_1: {}\n", fmt::join(level_1, "\n\t - "));
    }
    
    std::string const json_text = R"(
    {
        "level_1": {
            "level_1_1": [
                [1, "text", 0, 1, 2],
                [2, "text", 1, 3, 4],
                [3, "text", 5, 6, 8],
                [7, "text", 5, 4, 3]],
            "level_1_2": [
                [9, "text", 8, 9, 10],
                [10, "text", 9, 11, 12],
                [11, "text", 13, 14, 16],
                [15, "text", 13, 12, 11]],
            "level_1_3": [
                [17, "text", 16, 17, 18],
                [18, "text", 17, 19, 20],
                [19, "text", 21, 22, 24],
                [23, "text", 21, 20, 19]]
        }
    })";
    

    Prints

    level_1: {"text";8;9;10}
             - {"text";9;11;12}
             - {"text";13;14;16}
             - {"text";13;12;11}
    

    Prints

    Level 1:
         - {"text";8;9;10}
         - {"text";9;11;12}
         - {"text";13;14;16}
         - {"text";13;12;11}