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" }] }] }] }] }}
Full Listing
#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
.