boost::ptree find? or how to access deep arrays? C++ boost::ptree find? or how to access deep arrays? C++ json json

boost::ptree find? or how to access deep arrays? C++


As hinted in the linked answer I commented (Boost.PropertyTree subpath processing), you could write your own "selector" query, so you could write stuff like:

read_json("input.txt", pt);std::ostream_iterator<std::string> out(std::cout, ", ");std::cout << "\nSpecific children but in arrays: ";enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..FooBarDeepDeep6..FooBarValue2", out);std::cout << "\nSingle wildcard: ";enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..*..FooBarValue2", out);std::cout << "\nTwo wildcards: ";enumerate_path(pt, "Foo.Bar..FooBar..*..*..FooBarValue2", out);

The enumerate_path function need not be too complicated and takes any output iterator (so you can back_inserter(some_vector) just as well):

template <typename Tree, typename Out, typename T = std::string>Out enumerate_path(Tree const& pt, typename Tree::path_type path, Out out) {    if (path.empty())        return out;    if (path.single()) {        *out++ = pt.template get<T>(path);    } else {        auto head = path.reduce();        for (auto& child : pt) {            if (head == "*" || child.first == head) {                out = enumerate_path(child.second, path, out);            }        }    }    return out;}

As simple working demo prints:

Specific children but in arrays: andthis6, Single wildcard: andthis6, andthis7, andthis8, andthis9, Two wildcards: andthis1, andthis2, andthis3, andthis4, andthis6, andthis7, andthis8, andthis9, 

That is with the following input.txt:

{    "Foo": {        "nameofFoo": "foofoo",        "Bar": [{            "BarFoo": {                "BarFooDeep": {                    "BarFooDeepDeep": {                        "BarFooValue1": 123,                        "BarFooValue2": 456                    }                }            },            "FooBar": [{                "FooBarDeep0": [{                    "FooBarDeepDeep1": [{                        "FooBarValue1": "ineedthis1",                        "FooBarValue2": "andthis1"                    }],                    "FooBarDeepDeep2": [{                        "FooBarValue1": "ineedthis2",                        "FooBarValue2": "andthis2"                    }]                },                {                    "FooBarDeepDeep3": [{                        "FooBarValue1": "ineedthis3",                        "FooBarValue2": "andthis3"                    }],                    "FooBarDeepDeep4": [{                        "FooBarValue1": "ineedthis4",                        "FooBarValue2": "andthis4"                    }]                }],                "FooBarDeep1": [{                    "FooBarDeepDeep6": [{                        "FooBarValue1": "ineedthis6",                        "FooBarValue2": "andthis6"                    }],                    "FooBarDeepDeep7": [{                        "FooBarValue1": "ineedthis7",                        "FooBarValue2": "andthis7"                    }]                },                {                    "FooBarDeepDeep8": [{                        "FooBarValue1": "ineedthis8",                        "FooBarValue2": "andthis8"                    }],                    "FooBarDeepDeep9": [{                        "FooBarValue1": "ineedthis9",                        "FooBarValue2": "andthis9"                    }]                }]            }]        }]    }}

Live On Coliru

Full Listing

Live On Coliru

#include <boost/property_tree/ptree.hpp>#include <boost/property_tree/json_parser.hpp>#include <iostream>template <typename Tree, typename Out, typename T = std::string>Out enumerate_path(Tree const& pt, typename Tree::path_type path, Out out) {    if (path.empty())        return out;    if (path.single()) {        *out++ = pt.template get<T>(path);    } else {        auto head = path.reduce();        for (auto& child : pt) {            if (head == "*" || child.first == head) {                out = enumerate_path(child.second, path, out);            }        }    }    return out;}int main() {    std::ostream_iterator<std::string> out(std::cout, ", ");    using namespace boost::property_tree;    ptree pt;    read_json("input.txt", pt);    std::cout << "\nSpecific children but in arrays: ";    enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..FooBarDeepDeep6..FooBarValue2", out);    std::cout << "\nSingle wildcard: ";    enumerate_path(pt, "Foo.Bar..FooBar..FooBarDeep1..*..FooBarValue2", out);    std::cout << "\nTwo wildcards: ";    enumerate_path(pt, "Foo.Bar..FooBar..*..*..FooBarValue2", out);}


find() is for retrieving a child node by key; it doesn't search the whole ptree. It returns an assoc_iterator (or const_assoc_iterator), which you can convert to an iterator via the to_iterator() method on the parent:

ptree::const_assoc_iterator assoc = jsonPT.find("FooBarValue1");ptree::const_iterator myIT = jsonPT.to_iterator(assoc);

To search the ptree, you'll need to iterate it recursively:

struct Searcher {    struct Path { std::string const& key; Path const* prev; };    void operator()(ptree const& node, Path const* path = nullptr) const {        auto it = node.find("FooBarValue1");        if (it == node.not_found()) {            for (auto const& child : node) {  // depth-first search                Path next{child.first, path};                (*this)(child.second, &next);            }        } else {    // found "FooBarValue1"            double mlat = boost::lexical_cast<int>(myIT->second.data());            // ...            std::cout << "Mlat: " << mlat << std::endl;            std::cout << "Path (reversed): ";            for (Path const* p = path; p != nullptr; p = p->prev)                std::cout << p << ".";            std::cout << std::endl;        }    }};Searcher{}(jsonPT);

Alternatives for writing the recursive traversal would be a C++14 generic lambda, or in C++11 a type-erased concrete lambda using std::function.