Sorting JSON values alphabetically C++ - c++

If my json file is this:
[
{
"friendName": "Ann",
"birthday": "1990-04-19",
"favoriteColor": "Purple",
},
{
"friendName": "Rachel",
"birthday": "1995-7-05",
"favoriteColor": "Pink",
},
{
"friendName": "Max",
"birthday": "1993-10-07",
"favoriteColor": "Purple",
},
{
"friendName": "Bob",
"birthday": "1992-02-20",
"favoriteColor": "Red",
}
]
How could I obtain the youngest girl's name? (As in sorting the girls' birthday strings in descending order, then grabbing the first object in the list (1993-10-07) and printing their name).
I am using JSON for Modern C++ (https://github.com/nlohmann/json) and Xcode (version 6).
In my project, I will not know the amount of objects I will have. Is there a way to sort these strings by comparison?

A nice thing about the nlohmann/json is that it offers STL-like access, which allows for using a nlohmann::json with most of the functions in <algorithm>.
In particular, std::min_element() might come in handy when looking for the min element in a container... given a custom comparison function :)
In the following code, I used the dob_comp() lambda as a comparison function to compare persons by their dates of birth (dob). The youngest person is, therefore, the one with the "minimum date of birth".
[run it online (close main.cpp if it's open, click compile, then execute)]
// compile with: g++ --std=c++11 this_file.cpp -o your_executable_name
#include <algorithm>
#include <ctime>
#include <iomanip>
#include <iostream>
#include "json.hpp"
int main()
{
// sample json object
nlohmann::json j = nlohmann::json::parse("["
"{"
" \"friendName\": \"Ann\","
" \"birthday\": \"1990-04-19\","
" \"favoriteColor\": \"Purple\""
"},"
"{"
" \"friendName\": \"Rachel\","
" \"birthday\": \"1995-07-05\","
" \"favoriteColor\": \"Pink\""
"},"
"{"
" \"friendName\": \"Max\","
" \"birthday\": \"1993-10-07\","
" \"favoriteColor\": \"Purple\""
"},"
"{"
" \"friendName\": \"Bob\","
" \"birthday\": \"1992-02-20\","
" \"favoriteColor\": \"Red\""
"}"
"]");
// converts a date string to a std::tm structure
// assumes the string is formatted as "YYYY-MM-DD"
const auto str_to_time = [] (std::string str) {
std::tm tm;
// http://stackoverflow.com/a/21021900
//std::stringstream ss(str);
//ss >> std::get_time(&tm, "%Y-%m-%d");
strptime(str.c_str(), "%Y-%m-%d", &tm);
return tm;
};
// simplistic comparison of std::tm structures -- compares only the (year,month,day)
const auto time_comp = [] (const std::tm& t1, const std::tm& t2) {
if (t1.tm_year < t2.tm_year)
{
return true;
}
else if (t1.tm_year > t2.tm_year)
{
return false;
}
else if (t1.tm_mon < t2.tm_mon)
{
return true;
}
else if (t1.tm_mon > t2.tm_mon)
{
return false;
}
else if (t1.tm_mday < t2.tm_mday)
{
return true;
}
else if (t1.tm_mday > t2.tm_mday)
{
return false;
}
else
{
return true;
}
};
// I didn't have time to read too much of the "json.hpp" header
// so I used a quick "decltype()" to find the iterator type
using json_iterator_type = decltype(*j.begin());
// compares the DatesOfBirth (dob) of two persons
const auto dob_comp = [&str_to_time, &time_comp] (const json_iterator_type p1, const json_iterator_type p2) {
std::string dob1 = p1["birthday"];
std::string dob2 = p2["birthday"];
auto ttm1 = str_to_time(dob1);
auto ttm2 = str_to_time(dob2);
return time_comp(ttm1, ttm2);
};
// know your <algorithm>'s :)
const auto youngest = *std::min_element(j.begin(), j.end(), dob_comp);
std::cout << "The youngest person is: " << youngest << std::endl;
}
Note: if you want to sort the elements, you can use std::sort() like this:
std::sort(j.begin(), j.end(), dob_comp);
Note2: check out jq if you need a tool for processing json files.

Related

String incompatibility issues c++

I am trying to create a function that will take in a long string with " " and "," dividing different elements gained from a text file. "," represents a new line and " " represents the end of a number in the line. once split the strings should be part of an array.
string* splitstr(string text, string deli = " ")
{
int start = 0;
int end = text.find(deli);
string *numbers[end];
for (int i = 0; end != -1; i++)
{
numbers[i] = text.substr(start, end - start);
start = end + deli.size();
end = text.find(deli, start);
}
return numbers;
}
However I am facing a problem where I get this:
cannot convert 'std::string**' {aka 'std::__cxx11::basic_string<char>**'} to 'std::string*' {aka 'std::__cxx11::basic_string<char>*'} in return
How can I resolve this, or improve my function?
I was expecting to be able to store string elements in a string array and make the function return the array. I'm fairly new to programming and I started off with java so this logic might not work in c++.
You should avoid using arrays like this and instead use a vector
like as follows:
std::vector<std::string> splitstr(const std::string & text, const std::string & deli = " ")
{
int start = 0;
int end = text.find(deli);
std::vector<std::string> numbers;
for (int i = 0; end != -1; i++)
{
numbers.push_back(text.substr(start, end - start));
start = end + deli.size();
end = text.find(deli, start);
}
return numbers;
}
note this will not get the last item in the string if there is not a delimiter after it.
Use a combination of std::vector and std::string_view.
A string_view avoids the need to allocate new output strings, the caveat is that you can only use the split result if the original input string is still in memory.
#include <iostream>
#include <vector>
#include <string>
#include <string_view>
auto split(std::string_view string, std::string_view delimiters)
{
std::vector<std::string_view> substrings;
if (delimiters.size() == 0ul)
{
substrings.emplace_back(string);
return substrings;
}
auto start_pos = string.find_first_not_of(delimiters);
auto end_pos = start_pos;
auto max_length = string.length();
while (start_pos < max_length)
{
end_pos = std::min(max_length, string.find_first_of(delimiters, start_pos));
if (end_pos != start_pos)
{
substrings.emplace_back(&string[start_pos], end_pos - start_pos);
start_pos = string.find_first_not_of(delimiters, end_pos);
}
}
return substrings;
}
struct test_data_t
{
std::string input;
std::string delim;
};
int main()
{
using namespace std::string_view_literals;
std::vector<test_data_t> test_data
{
{ " ", " " },
{ "a", " " },
{ "aa", " " },
{ "a a", " " },
{ " a a", " " },
{ " a a", " " },
{ "a a ", " " },
{ "`The time has come,` the Walrus said, `To talk of many things : Of shoes -- and ships -- and sealing -- wax -- Of cabbages -- and kings -- And why the sea is boiling hot --And whether pigs have wings.`", "`, -.:" }
};
for (const auto& [input, delim] : test_data)
{
auto substrings = split(input, delim);
std::cout << substrings.size() << " : ";
bool comma = false;
for (const auto& substring : substrings)
{
if (comma) std::cout << ", ";
std::cout << substring;
comma = true;
}
std::cout << "\n";
}
return 0;
}

access json array values in c++

I'm new to c++ and trying to use nlohmann library and I'm quite stuck. I want to modify array of objects from json.
json =
{
"a": "xxxx",
"b": [{
"c": "aaa",
"d": [{
"e": "yyy"
},
{
"e": "sss",
"f": "fff"
}
]
}]
}
now I want to replace e value with "example" in the above structure. Could some one help me.
I tried to loop through the json structure and was able to read the "e" value but can't replace it. I tried: `
std::vector<std::string> arr_value;
std::ifstream in("test.json");
json file = json::parse(in);
for (auto& td : file["b"])
for (auto& prop : td["d"])
arr_value.push_back(prop["e"]);
//std::cout<<"prop" <<prop["e"]<< std::endl;
for (const auto& x : arr_value)
std::cout <<"value in vector string= " <<x<< "\n";
for (decltype(arr_value.size()) i = 0; i <= arr_value.size() - 1; i++)
{
std::string s = arr_value[i]+ "emp";
std::cout <<"changed value= " <<s << std::endl;
json js ;
js = file;
std::ofstream out("test.json");
js["e"]= s;
out << std::setw(4) << js << std::endl;
}
The following appends MODIFIED to every "e"-value and writes the result to test_out.json:
#include <vector>
#include <iostream>
#include <fstream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main() {
std::ifstream in("test.json");
json file = json::parse(in);
for (auto& td : file["b"])
for (auto& prop : td["d"]) {
prop["e"] = prop["e"].get<std::string>() + std::string(" MODIFIED");
}
std::ofstream out("test_out.json");
out << std::setw(4) << file << std::endl;
}
The prop["e"] = ... line does three things:
It gets the property with key "e",
Coerces it into a string using .get<std::string>() and appends "modified", and
writes back the result to prop["e"], which is a reference to the object nested in the JSON structure.

Find all keys JSON - RapidJSON

I need to find all the keys in the kTypeNames[] with rapidJSON library.
Trying to iterate all the nodes but I'm missing something; here's the code:
#include <iostream>
#include <fstream>
#include <string>
#include <bits/stdc++.h>
#include <unistd.h>
#include "rapidjson/document.h"
#include "rapidjson/writer.h"
#include "rapidjson/stringbuffer.h"
using namespace rapidjson;
using namespace std;
const char* kTypeNames[] = { "id", "text", "templ_text", "key" };
int main(int argc, char* argv[]) {
string line;
char json[65000];
std::ifstream file(argv[1]);
unsigned long i = 0;
if (file.is_open()) {
while (!file.eof()) {
file.get(json[i]);
i++;
}
file.close();
} else {
cout << "Unable to open file";
}
Document document;
document.Parse(json);
printf("\n\n\n\n*********Access values in document**********\n");
assert(document.IsObject());
for (auto Typename : kTypeNames) {
if (document.HasMember(Typename)) {
cout << "\n";
cout << Typename << ":" << document[Typename].GetString()<< endl;
cout << "\n";
}
else {
cout << "\n None\n";
}
}
It does not works with a nested JSON.
{
"node": {
"text": "find this",
"templ_text": "don't find",
"ver": "don't find"
},
"ic": "",
"text": "also this",
"templ_text": "don't care",
"par": {
"SET": {
"vis": "<blabla>",
"text": "keyFound",
"templ_text": "don't need this"
}
}
}
This is the output:
None
text:also this
templ_text:don't care
None
I would like to find all the "text" keys
How can I iterate through all the nodes/ json document?
The code you have is just searching for a list of pre-defined keys directly within the document root (document.HasMember is not a recursive search!).
You could just loop through the document nodes recursively. For example for object/map nodes, you loop on the MemberBegin() and MemberEnd() iterators, similar to a std::map or other standard containers.
for (auto i = node.MemberBegin(); i != node.MemberEnd(); ++i)
{
std::cout << "key: " << i->name.GetString() << std::endl;
WalkNodes(i->value);
}
Array uses Begin() and End(). Then, when you encounter a node with a "text" member, you can output the value of that node (i->value).
Alternatively, rather than using a Document DOM object, you can do it with the parser stream. Rapidjson uses a "push" API for this, where it calls methods you define in a class as it encounters each piece of JSON. Specifically, it will call a Key method.
class MyHandler : public BaseReaderHandler<UTF8<>, MyReader> {
bool Key(const char* str, SizeType length, bool copy)
{
std::cout << "Key: " << str << std::endl;
}
...
};
MyHandler handler;
rapidjson::Reader reader;
rapidjson::StringStream ss(json);
reader.Parse(ss, handler);
This gets a bit more complex, you will want to set a flag of some sorts, and then output the next value callback after.
class MyHandler : public BaseReaderHandler<UTF8<>, MyReader> {
bool Key(const char* str, SizeType length, bool copy)
{
isTextKey = strcmp(str, "text") == 0; // Also need to set to false in some other places
return true;
}
bool String(const char* str, SizeType length, bool copy)
{
if (isTextKey) std::cout << "text string " << str << std::endl;
return true;
}
...
bool isTextKey = false;
};
Also remember, that JSON allows a null within a string \0, which is why also have the size parameters and members, as well as Unicode. So to fully support any JSON document that needs accounting for.

Boost Karma generator for composition of classes

I've the following class diagram:
There's some unused class like BinaryOperator, but my real code needs them so I want to keep them also in the example.
I want to use boost::karma in order to obtain a JSON representation of this. The JSON should be like the following one:
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "4"
},
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "5"
},
{
"name": "IntegerValue",
"type": "Value",
"value": "6"
}
]
}
]
}
Since it's a simple example, I'd like to use BOOST_FUSION_ADAPT_ADT macro for my classes in order to modularize the generator.
I'm new to Karma, I've read the tutorial on boost site but I don't understand how to attack my problem. I can't find some good tutorial about that macro.
I don't want to use existing libraries for JSON because at first I want to learn Karma, and in second place JSON is only an example, I need to export my expression in many formats, and I can do it by simply changing generators while the code that uses BOOST_FUSION_ADAPT_ADT for my classes should be the same.
You can find the code for creating a sample expression. Where I need to start in order to solve my problem?
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
class Expression {
public:
virtual std::string getName() const = 0;
};
class Value : public Expression {
public:
virtual std::string getValue() const = 0;
};
class IntegerValue : public Value {
public:
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
class Function : public Expression {
public:
void addArgument(Expression* expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression*> m_arguments;
std::string m_name;
};
class Plus : public Function {
public:
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv) {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function* plus1 = new Plus();
Function* plus2 = new Plus();
Value* iv4 = new IntegerValue(4);
Value* iv5 = new IntegerValue(5);
Value* iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
return 0;
}
I'd advise against using Karma to generate JSON. I'd advise strongly against ADAPT_ADT (it's prone to very subtle UB bugs and it means you're trying to adapt something that wasn't designed for it. Just say no).
Here's my take on it. Let's take the high road and be as unintrusive as possible. That means
We can't just overload operator<< to print json (because you may want to naturally print the expressions instead)
It also means that what ever function is responsible for generating the JSON doesn't
have to bother with json implementation details
have to bother with pretty formatting
Finally, I wouldn't want to intrude on the expression tree with anything JSON specific. The most that could be acceptable is an opaque friend declaration.
A simple JSON facility:
This might well be the most simplistic JSON representation, but it does the required subset and makes a number of smart choices (supporting duplicate properties, retaining property order for example):
#include <boost/variant.hpp>
namespace json {
// adhoc JSON rep
struct Null {};
using String = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
std::vector<boost::recursive_variant_>,
std::vector<std::pair<String, boost::recursive_variant_> >
>::type;
using Property = std::pair<String, Value>;
using Object = std::vector<Property>;
using Array = std::vector<Value>;
}
That's all. This is fully functional. Let's prove it
Pretty Printing JSON
Like with the Expression tree itself, let's not hardwire this, but instead create a pretty-printing IO manipulator:
#include <iomanip>
namespace json {
// pretty print it
struct pretty_io {
using result_type = void;
template <typename Ref>
struct manip {
Ref ref;
friend std::ostream& operator<<(std::ostream& os, manip const& m) {
pretty_io{os,""}(m.ref);
return os;
}
};
std::ostream& _os;
std::string _indent;
void operator()(Value const& v) const {
boost::apply_visitor(*this, v);
}
void operator()(Null) const {
_os << "null";
}
void operator()(String const& s) const {
_os << std::quoted(s);
}
void operator()(Property const& p) const {
_os << '\n' << _indent; operator()(p.first);
_os << ": "; operator()(p.second);
}
void operator()(Object const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "{";
bool first = true;
for (auto& p : o) { first||_os << ","; nested(p); first = false; }
_os << "\n" << _indent << "}";
}
void operator()(Array const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "[\n" << _indent << " ";
bool first = true;
for (auto& p : o) { first||_os << ",\n" << _indent << " "; nested(p); first = false; }
_os << "\n" << _indent << "]";
}
};
Value to_json(Value const& v) { return v; }
template <typename T, typename V = decltype(to_json(std::declval<T const&>()))>
pretty_io::manip<V> pretty(T const& v) { return {to_json(v)}; }
}
The to_json thing dubs as a handy ADL-enabled extension point, you can already us it now:
std::cout << json::pretty("hello world"); // prints as a JSON String
Connecting it up
To make the following work:
std::cout << json::pretty(plus1);
All we need is the appropriate to_json overload. We could jot it all in there, but we might end up needing to "friend" a function named to_json, worse still, forward declare types from the json namespace (json::Value at the very least). That's too intrusive. So, let's add anothe tiny indirection:
auto to_json(Expression const* expression) {
return serialization::call(expression);
}
The trick is to hide the JSON stuff inside an opaque struct that we can then befriend: struct serialization. The rest is straightforward:
struct serialization {
static json::Value call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
json::Array args;
for (auto& a : f->m_arguments)
args.push_back(call(a));
return json::Object {
{ "name", f->getName() },
{ "type", "Function" },
{ "arguments", args },
};
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return json::Object {
{ "name", v->getName() },
{ "type", "Value" },
{ "value", v->getValue() },
};
}
return {}; // Null in case we didn't implement a node type
}
};
Full Demo
See it Live On Coliru
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <iomanip>
#include <vector>
struct Expression {
virtual std::string getName() const = 0;
};
struct Value : Expression {
virtual std::string getValue() const = 0;
};
struct IntegerValue : Value {
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
struct Function : Expression {
void addArgument(Expression *expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression *> m_arguments;
std::string m_name;
friend struct serialization;
};
struct Plus : Function {
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
// A simple JSON facility
#include <boost/variant.hpp>
namespace json {
// adhoc JSON rep
struct Null {};
using String = std::string;
using Value = boost::make_recursive_variant<
Null,
String,
std::vector<boost::recursive_variant_>,
std::vector<std::pair<String, boost::recursive_variant_> >
>::type;
using Property = std::pair<String, Value>;
using Object = std::vector<Property>;
using Array = std::vector<Value>;
}
///////////////////////////////////////////////////////////////////////////////
// Pretty Print manipulator
#include <iomanip>
namespace json {
// pretty print it
struct pretty_io {
using result_type = void;
template <typename Ref>
struct manip {
Ref ref;
friend std::ostream& operator<<(std::ostream& os, manip const& m) {
pretty_io{os,""}(m.ref);
return os;
}
};
std::ostream& _os;
std::string _indent;
void operator()(Value const& v) const {
boost::apply_visitor(*this, v);
}
void operator()(Null) const {
_os << "null";
}
void operator()(String const& s) const {
_os << std::quoted(s);
}
void operator()(Property const& p) const {
_os << '\n' << _indent; operator()(p.first);
_os << ": "; operator()(p.second);
}
void operator()(Object const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "{";
bool first = true;
for (auto& p : o) { first||_os << ","; nested(p); first = false; }
_os << "\n" << _indent << "}";
}
void operator()(Array const& o) const {
pretty_io nested{_os, _indent+" "};
_os << "[\n" << _indent << " ";
bool first = true;
for (auto& p : o) { first||_os << ",\n" << _indent << " "; nested(p); first = false; }
_os << "\n" << _indent << "]";
}
};
Value to_json(Value const& v) { return v; }
template <typename T, typename V = decltype(to_json(std::declval<T const&>()))>
pretty_io::manip<V> pretty(T const& v) { return {to_json(v)}; }
}
///////////////////////////////////////////////////////////////////////////////
// Expression -> JSON
struct serialization {
static json::Value call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
json::Array args;
for (auto& a : f->m_arguments)
args.push_back(call(a));
return json::Object {
{ "name", f->getName() },
{ "type", "Function" },
{ "arguments", args },
};
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return json::Object {
{ "name", v->getName() },
{ "type", "Value" },
{ "value", v->getValue() },
};
}
return {};
}
};
auto to_json(Expression const* expression) {
return serialization::call(expression);
}
int main() {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function *plus1 = new Plus();
Function *plus2 = new Plus();
Value *iv4 = new IntegerValue(4);
Value *iv5 = new IntegerValue(5);
Value *iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
std::cout << json::pretty(plus1);
}
Output is picture-perfect from your question:
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "4"
},
{
"name": "Plus",
"type": "Function",
"arguments": [
{
"name": "IntegerValue",
"type": "Value",
"value": "5"
},
{
"name": "IntegerValue",
"type": "Value",
"value": "6"
}
]
}
]
}
Thanks, the fact is that json is only one of many formats that I must to use, some format is proprietary and there are no libraries, so I want to use an uniform way for all. I've decided to use json for the question because is known to community more than, for example, asciimath or other formats created by us – Jepessen 9 hours ago
This changes nothing about my recommendation. If anything, it really emphasizes that you don't want arbitrary restrictions imposed.
The problems with Karma
Karma is an "inline" DSL for statically generated generators. They work well for statically typed things. Your AST uses dynamic polymorphism.
That removes any chance of writing a succinct generator barring the use of many, complicated semantic actions. I don't remember writing many explicit answers related to Karma, but the problems with both dynamic polymorphism and semantic actions are much the same on the Qi side:
How can I use polymorphic attributes with boost::spirit::qi parsers?
Boost Spirit: "Semantic actions are evil"?
The key draw backs all apply, except obviously that AST creation is not happening, so the performance effect of allocations is less severe than with Qi parsers.
However, the same logic still stands: Karma generators are statically combined for efficiency. However your dynamic type hierarchy precludes most of that efficiency. In other words, you are not the target audience for Karma.
Karma has another structural limitation that will bite here, regardless of the way your AST is designed: it's (very) hard to make use of stateful rules to do pretty printing.
This is, for me, a key reason to practically never use Karma. Even if pretty printing isn't a goal you can still get similar mileage just generating output visiting the AST using Boost Fusion directly (we used this in our project to generate different versions of OData XML and JSON representations of API types for use in restful APIs).
Granted, there are some stateful generating tasks that have custom directives builtin to Karma, and sometimes they hit the sweet spot for rapid prototyping, e.g.
Writing a Boost ublas matrix to a text file
Though it gets wieldy quickly Inconsistent Generator directive column behavior in boost karma. Many quirks arise due to the way sub-generators are "counted" w.r.t. the columns[] directive
Let's Do It Anyways
Because I'm not a masochist, I'll do borrow a concept from the other answer: creating an intermediate representation that facilitates Karma a lot better.
In this sample the intermediate representation can be exceedingly simple, but I suspect your other requirements like "for example, asciimath or other formats created by us" will require a more detailed design.
///////////////////////////////////////////////////////////////////////////////
// A simple intermediate representation
#include <boost/variant.hpp>
namespace output_ast {
struct Function;
struct Value;
using Expression = boost::variant<Function, Value>;
using Arguments = std::vector<Expression>;
struct Value { std::string name, value; };
struct Function { std::string name; Arguments args; };
}
Firstly, because we're going to use Karma, we do need to actually adapt the intermediate representation:
#include <boost/fusion/include/struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(output_ast::Value, name, value)
BOOST_FUSION_ADAPT_STRUCT(output_ast::Function, name, args)
A generator
Here's the simplest generator I can think of, give and take 2 things:
I have tweaked it for considerable time to get some "readable" format. It gets simpler if you remove all insignificant whitespace.
I opted to not store redundant information (such as the static "type" representation in the intermediate representation). Doing so would slightly uncomplicate, mostly by making the type rule more similar to name and value.
namespace karma_json {
namespace ka = boost::spirit::karma;
template <typename It>
struct Generator : ka::grammar<It, output_ast::Expression()> {
Generator() : Generator::base_type(expression) {
expression = function|value;
function
= "{\n " << ka::delimit(",\n ")
[name << type(+"Function") ]
<< arguments
<< "\n}"
;
arguments = "\"arguments\": [" << -(("\n " << expression) % ",") << ']';
value
= "{\n " << ka::delimit(",\n ")
[name << type(+"Value") ]
<< value_
<< "\n}"
;
type = "\"type\":\"" << ka::string(ka::_r1) << "\"";
string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
name = "\"name\":" << string;
value_ = "\"value\":" << string;
}
private:
ka::rule<It, output_ast::Expression()> expression;
ka::rule<It, output_ast::Function()> function;
ka::rule<It, output_ast::Arguments()> arguments;
ka::rule<It, output_ast::Value()> value;
ka::rule<It, std::string()> string, name, value_;
ka::rule<It, void(std::string)> type;
};
}
Post Scriptum
I was making the simplified take for completeness. And ran into this excellent demonstration of completely unobvious attribute handling quirks. The following (just stripping whitespace handling) does not work:
function = '{' << ka::delimit(',') [name << type] << arguments << '}';
value = '{' << ka::delimit(',') [name << type] << value_ << '}' ;
You can read the error novel here in case you like drama. The problem is that the delimit[] block magically consolidates the attributes into a single string (huh). The error message reflects that the string attribute has not been consumed when e.g. starting the arguments generator.
The most direct way to treat the symptom would be to break up the attribute, but there's no real way:
function = '{' << ka::delimit(',') [name << ka::eps << type] << arguments << '}';
value = '{' << ka::delimit(',') [name << ka::eps << type] << value_ << '}' ;
No difference
function = '{' << ka::delimit(',') [ka::as_string[name] << ka::as_string[type]] << arguments << '}';
value = '{' << ka::delimit(',') [ka::as_string[name] << ka::as_string[type]] << value_ << '}' ;
Would be nice if it actually worked. No amount of adding includes or replacing with incantations like ka::as<std::string>()[...] made the compilation error go away.²
So, to just end this sob-story, we'll stoop to the mind-numbingly tedious:
function = '{' << name << ',' << type << ',' << arguments << '}';
arguments = "\"arguments\":[" << -(expression % ',') << ']';
See the section labeled "Simplified Version" below for the live demo.
Using it
The shortest way to generate using that grammar is to create the intermediate representation:
///////////////////////////////////////////////////////////////////////////////
// Expression -> output_ast
struct serialization {
static output_ast::Expression call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
output_ast::Arguments args;
for (auto& a : f->m_arguments) args.push_back(call(a));
return output_ast::Function { f->getName(), args };
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return output_ast::Value { v->getName(), v->getValue() };
}
return {};
}
};
auto to_output(Expression const* expression) {
return serialization::call(expression);
}
And use that:
using It = boost::spirit::ostream_iterator;
std::cout << format(karma_json::Generator<It>{}, to_output(plus1));
Full Demo
Live On Wandbox¹
#include <boost/lexical_cast.hpp>
#include <iostream>
#include <vector>
struct Expression {
virtual std::string getName() const = 0;
};
struct Value : Expression {
virtual std::string getValue() const = 0;
};
struct IntegerValue : Value {
IntegerValue(int value) : m_value(value) {}
virtual std::string getName() const override { return "IntegerValue"; }
virtual std::string getValue() const override { return boost::lexical_cast<std::string>(m_value); }
private:
int m_value;
};
struct Function : Expression {
void addArgument(Expression *expression) { m_arguments.push_back(expression); }
virtual std::string getName() const override { return m_name; }
protected:
std::vector<Expression *> m_arguments;
std::string m_name;
friend struct serialization;
};
struct Plus : Function {
Plus() : Function() { m_name = "Plus"; }
};
///////////////////////////////////////////////////////////////////////////////
// A simple intermediate representation
#include <boost/variant.hpp>
namespace output_ast {
struct Function;
struct Value;
using Expression = boost::variant<Function, Value>;
using Arguments = std::vector<Expression>;
struct Value { std::string name, value; };
struct Function { std::string name; Arguments args; };
}
#include <boost/fusion/include/struct.hpp>
BOOST_FUSION_ADAPT_STRUCT(output_ast::Value, name, value)
BOOST_FUSION_ADAPT_STRUCT(output_ast::Function, name, args)
#include <boost/spirit/include/karma.hpp>
namespace karma_json {
namespace ka = boost::spirit::karma;
template <typename It>
struct Generator : ka::grammar<It, output_ast::Expression()> {
Generator() : Generator::base_type(expression) {
expression = function|value;
function
= "{\n " << ka::delimit(",\n ")
[name << type(+"Function") ]
<< arguments
<< "\n}"
;
arguments = "\"arguments\": [" << -(("\n " << expression) % ",") << ']';
value
= "{\n " << ka::delimit(",\n ")
[name << type(+"Value") ]
<< value_
<< "\n}"
;
type = "\"type\":\"" << ka::string(ka::_r1) << "\"";
string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
name = "\"name\":" << string;
value_ = "\"value\":" << string;
}
private:
ka::rule<It, output_ast::Expression()> expression;
ka::rule<It, output_ast::Function()> function;
ka::rule<It, output_ast::Arguments()> arguments;
ka::rule<It, output_ast::Value()> value;
ka::rule<It, std::string()> string, name, value_;
ka::rule<It, void(std::string)> type;
};
}
///////////////////////////////////////////////////////////////////////////////
// Expression -> output_ast
struct serialization {
static output_ast::Expression call(Expression const* e) {
if (auto* f = dynamic_cast<Function const*>(e)) {
output_ast::Arguments args;
for (auto& a : f->m_arguments) args.push_back(call(a));
return output_ast::Function { f->getName(), args };
}
if (auto* v = dynamic_cast<Value const*>(e)) {
return output_ast::Value { v->getName(), v->getValue() };
}
return {};
}
};
auto to_output(Expression const* expression) {
return serialization::call(expression);
}
int main() {
// Build expression 4 + 5 + 6 as 4 + (5 + 6)
Function *plus1 = new Plus();
Function *plus2 = new Plus();
Value *iv4 = new IntegerValue(4);
Value *iv5 = new IntegerValue(5);
Value *iv6 = new IntegerValue(6);
plus2->addArgument(iv5);
plus2->addArgument(iv6);
plus1->addArgument(iv4);
plus1->addArgument(plus2);
// Generate json string here, but how?
using It = boost::spirit::ostream_iterator;
std::cout << format(karma_json::Generator<It>{}, to_output(plus1));
}
The Output
The generator is being as as readable/robust/functional as I'd like (there are quirks related to delimiters, there are issues when type contain characters that would need to be quoted, there's no stateful indentation).
The result doesn't look as expected, though it's valid JSON:
{
"name":"Plus",
"type":"Function",
"arguments": [
{
"name":"IntegerValue",
"type":"Value",
"value":"4"
},
{
"name":"Plus",
"type":"Function",
"arguments": [
{
"name":"IntegerValue",
"type":"Value",
"value":"5"
},
{
"name":"IntegerValue",
"type":"Value",
"value":"6"
}]
}]
}
Fixing it is... a nice challenge if you want to try it.
The Simplified Version
The simplified version, complete with attribute-handling workaround documented above:
Live On Coliru
namespace karma_json {
namespace ka = boost::spirit::karma;
template <typename It>
struct Generator : ka::grammar<It, output_ast::Expression()> {
Generator() : Generator::base_type(expression) {
expression = function|value;
function = '{' << name << ',' << type << ',' << arguments << '}';
arguments = "\"arguments\":[" << -(expression % ',') << ']';
value = '{' << name << ',' << type << ',' << value_ << '}' ;
string = '"' << *('\\' << ka::char_("\\\"") | ka::char_) << '"';
type = "\"type\":" << string;
name = "\"name\":" << string;
value_ = "\"value\":" << string;
}
private:
ka::rule<It, output_ast::Expression()> expression;
ka::rule<It, output_ast::Function()> function;
ka::rule<It, output_ast::Arguments()> arguments;
ka::rule<It, output_ast::Value()> value;
ka::rule<It, std::string()> string, name, type, value_;
};
}
Yields the following output:
{"name":"Plus","type":"Function","arguments":[{"name":"IntegerValue","type":"Value","value":"4"},{"name":"Plus","type":"Function","arguments":[{"name":"IntegerValue","type":"Value","value":"5"},{"name":"IntegerValue","type":"Value","value":"6"}]}]}
I'm inclined to think this is a much better cost/benefit ratio than the failed attempt at "pretty" formatting. But the real story here is that the maintenance cost is through the roof anyways.
¹ Interestingly, Coliru exceeds the compilation time... This too could be an argument guiding your design descisions
² makes you wonder how many people actually use Karma day-to-day

How to iterate a boost property tree?

I am know approaching to boost property tree and saw that it is a good feature of boost libs for c++ programming.
Well, I have one doubt? how to iterate a property tree using iterators or similar?
In reference there is just an example of browsing the tree through:
BOOST_FOREACH
But is there nothing more? Something like an stl-like container? It would be a better solution, speaking about code quality....
Here is what I came up with after much experimentation. I wanted to share it in the community because I couldn't find what I wanted. Everybody seemed to just post the answer from the boost docs, which I found to be insufficient. Anyhow:
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <string>
#include <iostream>
using namespace std;
using boost::property_tree::ptree;
string indent(int level) {
string s;
for (int i=0; i<level; i++) s += " ";
return s;
}
void printTree (ptree &pt, int level) {
if (pt.empty()) {
cerr << "\""<< pt.data()<< "\"";
}
else {
if (level) cerr << endl;
cerr << indent(level) << "{" << endl;
for (ptree::iterator pos = pt.begin(); pos != pt.end();) {
cerr << indent(level+1) << "\"" << pos->first << "\": ";
printTree(pos->second, level + 1);
++pos;
if (pos != pt.end()) {
cerr << ",";
}
cerr << endl;
}
cerr << indent(level) << " }";
}
return;
}
int main(int, char*[]) {
// first, make a json file:
string tagfile = "testing2.pt";
ptree pt1;
pt1.put("object1.type","ASCII");
pt1.put("object2.type","INT64");
pt1.put("object3.type","DOUBLE");
pt1.put("object1.value","one");
pt1.put("object2.value","2");
pt1.put("object3.value","3.0");
write_json(tagfile, pt1);
ptree pt;
bool success = true;
try {
read_json(tagfile, pt);
printTree(pt, 0);
cerr << endl;
}catch(const json_parser_error &jpe){
//do error handling
success = false
}
return success;
}
Here is the output:
rcook#rzbeast (blockbuster): a.out
{
"object1":
{
"type": "ASCII",
"value": "one"
},
"object2":
{
"type": "INT64",
"value": "2"
},
"object3":
{
"type": "DOUBLE",
"value": "3.0"
}
}
rcook#rzbeast (blockbuster): cat testing2.pt
{
"object1":
{
"type": "ASCII",
"value": "one"
},
"object2":
{
"type": "INT64",
"value": "2"
},
"object3":
{
"type": "DOUBLE",
"value": "3.0"
}
}
BOOST_FOREACH is just a convenient way for iterating that can be done by iterator, begin() and end()
Your_tree_type::const_iterator end = tree.end();
for (your_tree_type::const_iterator it = tree.begin(); it != end; ++it)
...
And since C++11 it's:
for (auto& it: tree)
...
I ran into this issue recently and found the answers incomplete for my need, so I came up with this short and sweet snippet:
using boost::property_tree::ptree;
void parse_tree(const ptree& pt, std::string key)
{
std::string nkey;
if (!key.empty())
{
// The full-key/value pair for this node is
// key / pt.data()
// So do with it what you need
nkey = key + "."; // More work is involved if you use a different path separator
}
ptree::const_iterator end = pt.end();
for (ptree::const_iterator it = pt.begin(); it != end; ++it)
{
parse_tree(it->second, nkey + it->first);
}
}
Important to note is that any node, except the root node can contain data as well as child nodes. The if (!key.empty()) bit will get the data for all but the root node, we can also start building the path for the looping of the node's children if any.
You'd start the parsing by calling parse_tree(root_node, "") and of course you need to do something inside this function to make it worth doing.
If you are doing some parsing where you don't need the FULL path, simply remove the nkey variable and it's operations, and just pass it->first to the recursive function.
An addition to the answer How to iterate a boost property tree? :
In the C++11 style range based for for (auto node : tree), each node is a std::pair<key_type, property_tree>
Whereas in the manually written iteration
Your_tree_type::const_iterator end = tree.end();
for (your_tree_type::const_iterator it = tree.begin(); it != end; ++it)
...
the iterator it is a pointer to such a pair. It's a tiny difference in usage. For example, to access the key, one would write it->first but node.first.
Posted as a new answer, because my proposed edit to the original answer was rejected with the suggestion to post a new answer.
BFS based print ptree traversal, May be used if we want to do some algorithmic manipulation
int print_ptree_bfs(ptree &tree) {
try {
std::queue<ptree*> treeQ;
std::queue<string> strQ;
ptree* temp;
if (tree.empty())
cout << "\"" << tree.data() << "\"";
treeQ.push(&tree);
//cout << tree.data();
strQ.push(tree.data());
while (!treeQ.empty()) {
temp = treeQ.front();
treeQ.pop();
if (temp == NULL) {
cout << "Some thing is wrong" << std::endl;
break;
}
cout << "----- " << strQ.front() << "----- " << std::endl;
strQ.pop();
for (auto itr = temp->begin(); itr != temp->end(); itr++) {
if (!itr->second.empty()) {
//cout << itr->first << std::endl;
treeQ.push(&itr->second);
strQ.push(itr->first);
} else {
cout<<itr->first << " " << itr->second.data() << std::endl;
}
}
cout << std::endl;
}
} catch (std::exception const& ex) {
cout << ex.what() << std::endl;
}
return EXIT_SUCCESS;
}