Parse XML with boost property tree - c++

I have the following XML file and I want to store it using the below structures.
the data structs:
struct transitions
{
string oldstate;
string event;
string newstate;
};
struct XML_Diagram
{
string diag_name;
string diag_defaultstate;
list<string> diag_states;
list<string> diag_events;
list<transitions> diag_transitions;
};
the xml file:
<diagram>
<diagname>DiagaX</diagname>
<states>
<state>A</state>
.............
</states>
<events>
<event>ev1</event>
.................
</events>
<defaultstate>A</defaultstate>
<transitions>
<transition>
<oldstate>A</oldstate>
<event>ev1</event>
<newstate>B</newstate>
</transition>
<transition>
<oldstate>B</oldstate>
<event>ev2</event>
<newstate>C</newstate>
</transition>
.........................
</transitions>
</diagram>
It is clear to me how can I to access diagram.states .
I can do it with the folowing code:
using boost::property_tree::ptree;
ptree pt;
// Get diagram states
BOOST_FOREACH(ptree::value_type &v, pt.get_child("diagram.states"))
{
diag_states.push_back(v.second.data());
}
What is not clear to me is how can I access the data from at the level diagram.transitions.transition ?
My problem is that I could not find any examples in the documentation on how to parse more complex xml files with several levels.

This useful utility function traverses and pretty-prints an entire property tree:
using boost::property_tree::ptree;
std::string q(const std::string& s) { return "\"" + s + "\""; }
void print_tree(const ptree& pt, int level)
{
const std::string sep(2 * level, ' ');
BOOST_FOREACH(const ptree::value_type &v, pt) {
std::cout
<< sep
<< q(v.first) << " : " << q(v.second.data()) << "\n";
print_tree(v.second, level + 1);
}
}
void print_tree(const ptree& pt) { print_tree(pt, 0); }
The v.second values are trees themselves that can be accessed with the usual get methods. The transitions
can for example be accessed and printed like this:
using std::string;
void print_transitions(const ptree& pt)
{
BOOST_FOREACH(
const ptree::value_type &v,
pt.get_child("diagram.transitions"))
{
const ptree& child = v.second;
std::cout
<< "Event "
<< child.get<string>("event")
<< " in state "
<< child.get<string>("oldstate")
<< " leads to state "
<< child.get<string>("newstate")
<< "\n";
}
}

Here is another example of how to print ptree with attributes:
namespace pt = boost::property_tree;
// worker
typedef std::pair<const pt::ptree&, unsigned> tree_printer;
// empty ptree helper
const pt::ptree& empty_ptree()
{
static pt::ptree t;
return t;
}
std::ostream& operator <<(std::ostream& os, const tree_printer& p)
{
const pt::ptree& tree = p.first;
if(tree.empty()) return os;
const std::string indent(p.second, ' ');
BOOST_FOREACH(const pt::ptree::value_type& v, tree)
{
const std::string& nodeName = v.first;
if(nodeName == "<xmlattr>") continue;
os << indent << nodeName;
const pt::ptree& attributes =
v.second.get_child("<xmlattr>", empty_ptree());
if(!attributes.empty())
{
os << " [ ";
BOOST_FOREACH(const pt::ptree::value_type& attr, attributes)
{
const std::string& attrName = attr.first;
const std::string& attrVal = attr.second.data();
os << attrName << " = '" << attrVal << "'; ";
}
os << "]";
}
os << "\n";
const pt::ptree& childNode = v.second;
os << tree_printer(childNode, p.second + 1);
}
return os;
}
std::ostream& operator <<(std::ostream& os, const pt::ptree& tree)
{
os << tree_printer(tree, 0);
return os;
}
Hope this helps.

Related

Creating a sticky manipulator that inserts a delimiter between each token of an output stream

I've seen examples that allow you to create a manipulator that inserts delimiters but none of those manipulators are sticky. That is, the manipulator returns a special class that inserts the delimiter, rather than modifying the output stream permanently so that it can do it on its own.
I want to be able to do this:
std::cout << sep(", ");
std::cout << "hello" << "world";
// "hello, world"
At the moment this prints "h, e, l, l, o, w, o, r, l, d" when I need it to be "hello, world". The reason I'm getting the wrong output is because I put the printing in the overflow() method and overflow() is being called for each character. I'm not sure where is the appropriate place to put it.
Sorry about it being verbose. If I knew a simpler way to write it I would. Just start from the bottom and work your way up:
#include <iostream>
#include <cstring>
// index for delimiter
int separator() {
static int idx = std::ios_base::xalloc();
return idx;
}
// index for storage of dynamically-allocated buffer
int rdbuffer() {
static int idx = std::ios_base::xalloc();
return idx;
}
struct custom_separator : std::streambuf {
public:
custom_separator(std::ostream& _stream)
: stream(_stream), buffer(_stream.rdbuf()) {}
int_type overflow(int_type c) {
// has a token been read already
if (token_read) {
char* delim = static_cast<char*>(stream.pword(separator()));
// print delim
buffer->sputn(delim, strlen(delim));
}
token_read = true;
return buffer->sputc(c);
}
private:
std::ostream& stream;
std::streambuf* buffer;
bool token_read = false;
};
// deletes the buffer and the delimiter
void cleanup(std::ios_base::event evt, std::ios_base& str, int idx) {
if (evt == std::ios_base::erase_event) {
delete static_cast<const char*>(str.pword(idx));
delete static_cast<custom_separator*>(str.pword(rdbuffer()));
}
}
std::ostream& set_separator(std::ostream& os, const char* str) {
if (!os.bad()) {
// If a separator string doesn't exist, assign os a buffer that prints one
if (!os.pword(separator())) {
auto buf = new custom_separator(os);
os.rdbuf(buf);
// this is to keep track of buf so we can delete it later
os.pword(rdbuffer()) = static_cast<void*>(buf);
os.register_callback(cleanup, separator());
}
// delete the previous separator
delete static_cast<const char*>(os.pword(separator()));
// store the new one
os.pword(separator()) = (void*)(str);
}
return os;
}
struct sep {
explicit sep(const char* _sep)
: separator(new char[strlen(_sep) + 1]) {
strcpy(separator, _sep);
}
sep(const sep&) = delete;
sep(const sep&&) = delete;
char* separator;
};
std::ostream& operator<<(std::ostream& os, const sep& manip) {
set_separator(os, manip.separator);
return os;
}
int main() {
std::cout << sep(", ");
std::cout << "hello";
std::cout << "world";
// "h, e, l, l, o, w, o, r, l, d"
}
The main issue with overflow() is that I don't know when to detect when the end of a token like "hello" has been read to know when to insert.
Try the following. Additionally, new line break processing (new line symbol) has been added so that the separator is not added during the transfer (after new line).
#include <iostream>
class MyStream
{
public:
struct Sep
{
Sep (const std::string& sep_value = ""):
m_sep(sep_value)
{
}
operator std::string()
{
return m_sep;
}
private:
std::string m_sep;
};
MyStream& operator << (const Sep& sep)
{
m_sep = sep;
m_add_sep = false;
return *this;
}
MyStream& operator << (const std::string& str)
{
if(str.find(MyStream::endl) != std::string::npos)
m_add_sep = false;
operator<< <std::string>(str);
m_add_sep = false;
return *this;
}
template <typename T>
MyStream& operator << (const T& value)
{
if(m_add_sep)
std::cout << static_cast<std::string>(m_sep);
std::cout << value;
m_add_sep = true;
return *this;
}
static const std::string endl;
private:
Sep m_sep;
bool m_add_sep = false;
};
const std::string MyStream::endl = std::string("\n");
int main()
{
MyStream stream;
stream << "hello" << "world" << MyStream::endl; // prints "helloworld"
stream << MyStream::Sep(", ");
stream << "hello" << "world" << MyStream::endl; // prints "hello, world"
stream << 1 << 2;
stream << 3 << MyStream::endl; // both lines prints "1, 2, 3"
stream << MyStream::Sep();
stream << 1 << 2 << 3 << MyStream::endl; // prints "123"
return 0;
}
Here's a possible solution. The separator is the same for all streams.
namespace alt_std
{
struct sep
{
sep(const char* s) { s_ = s; }
friend std::ostream& operator<<(std::ostream& os, const sep& sm)
{
return os;
}
inline static std::string s_{};
};
std::ostream& operator<<(std::ostream& os, const char* s)
{
return std::operator<<(os, s), std::operator<<(os, sep::s_);
}
}
int main()
{
using namespace alt_std;
std::cout << sep(" ") << "hello";
std::cout << "world" << std::endl; // "hello world\n"
std::cout << sep("") << "hel" << "lo"; // "hello"
}
If you want to do it at the stream object level, it's more difficult because stream objects don't have a custom storage space where you can store the value of the separator.
Or you could simply wrap the stream object:
template< typename OS >
struct wrapper
{
wrapper(OS& os, const char* sep) : os_{ os }, sep_{ sep } {}
template< typename T>
friend wrapper& operator<<(wrapper& os, const T& v)
{
os.os_ << v << os.sep_;
return os;
}
OS& os_;
std::string sep_;
};
int main()
{
wrapper os{ std::cout, " " };
os << "hello" << "world";
}

How to print a vector of pairs that have as first and second 2 classes in c++?

I have something like this where Client and Order are classes :
std::vector<std::pair<Client,Order>> pair;
pair.push_back(std::make_pair(Client(2,"Anca"),Order(3,1)));
pair.push_back(std::make_pair(Client(16,"Maria"),Order(1,3)));
pair.push_back(std::make_pair(Client(29,"Alex"),Order(10,5)));
class Client{
private:
int dateWhenOrderWasPlaced;
std::string clientName;
public:
Client(int date,std::string name){
dateWhenOrderWasPlaced=date;
clientName=name;
}
class Order{
private:
int amountPizza;
int pizzaAge;
public:
Order(int amPizza,int agePizza){
amountPizza=amPizza;
pizzaAge=agePizza;
}
And i can't figure out how to print this.I have tried in many ways :
void print(std::vector<std::pair<Client,Order>> pair){
for(const auto& it : pair){
std::cout << "First: "<<pair[it].first<< ", Second: " << pair[it].second <<std::endl;
}
}
And this :
void print(std::vector<std::pair<Client,Order>> pair){
for(const auto& it : pair){
std::cout << "First: "<<it.first<< ", Second: " << it.second <<std::endl;
}
}
And in the both ways i have error(first-no operator[] and second,no operator <<)
Your first attempt does not work because it is the actual pair but std::pair does not have an operator[]. Your second attempt is the correct way to go, but it does not work because you have not defined operator<< for your classes.
So, simply define operator<<, eg:
class Client
{
private:
int dateWhenOrderWasPlaced;
std::string clientName;
public:
Client(int date, std::string name)
: dateWhenOrderWasPlaced(date), clientName(name)
{
}
friend std::ostream& operator<<(std::ostream &os, const Client &c)
{
// print c.dateWhenOrderWasPlaced and c.clientName to os as needed...
return os;
}
};
class Order
{
private:
int amountPizza;
int pizzaAge;
public:
Order(int amPizza, int agePizza)
: amountPizza(amPizza), pizzaAge(agePizza)
{
}
friend std::ostream& operator<<(std::ostream &os, const Order &o)
{
// print o.amountPizza and o.pizzaAge to os as needed...
return os;
}
};
void print(const std::vector<std::pair<Client,Order>> &pair)
{
for(const auto& it : pair)
{
std::cout << "First: " << it.first << ", Second: " << it.second << std::endl;
}
}

Selection in the codomain of interval_map

In my current project processes, distinguishable intervals, needs to be combined, if they are adjacent.
For this purpose I wanted to use the fantastic boost::icl library. Every process can be uniquely identified by its id.
First I'm adding some intervals to my interval_map. Now I wanted to accomplish two things:
Iterate over all occurring process-types (Here id=1,4,7)
Secondly, iterate over all processes being in a certain subset of kinds, in such a way that merging of overlapping intervals is automatically done.
This is what I got so far:
#include <iostream>
#include <set>
#include <boost/icl/interval_map.hpp>
#include "boost/icl/closed_interval.hpp"
struct Process {
int id;
};
bool operator==(const Process& p, const Process& q) {
return p.id == q.id;
}
bool operator<(const Process& p, const Process& q) {
return p.id < q.id;
}
std::ostream& operator<<(std::ostream& str, const Process& p) {
str << "Process{" << p.id << "}";
return str;
}
int main(int, char**) {
using namespace boost::icl;
interval_map<double, std::set<Process>> imap;
imap.add({ interval<double>::closed(0., 4.),{ Process{ 4 } } });
imap.add({ interval<double>::closed(2., 6.),{ Process{ 1 } } });
imap.add({ interval<double>::closed(4., 9.),{ Process{ 4 } } });
imap.add({ interval<double>::closed(8., 8.),{ Process{ 7 } } });
for (auto&& iter : imap) {
std::cout << iter.first << " - " << iter.second<< std::endl;
}
for (auto iter : find(imap, { Process{4} })) { // How to implement find on codomain
// Should print:
// [0.,4.] - { Process{4}}
// [4.,9.] - { Process{4}}
std::cout << iter.first << " - " << iter.second << std::endl;
}
}
First, an observation, since the intervals are closed, [0,4] and [4,6] aren't actually adjacent, but overlapping. Did you mean right_open?
Second, the interval map models a function, the mapping is not guaranteed to be injective.
In the limited scope of your example, it seems you'd rather invert the datastructure, to arrive at:
#include "boost/icl/closed_interval.hpp"
#include <boost/icl/interval_map.hpp>
#include <iostream>
#include <set>
#include <map>
struct Process {
int id;
friend bool operator==(const Process& p, const Process& q) { return p.id == q.id; }
friend bool operator<(const Process& p, const Process& q) { return p.id < q.id; }
friend std::ostream& operator<<(std::ostream& str, const Process& p) {
return str << "Process{" << p.id << "}";
}
};
int main(int, char**) {
using namespace boost::icl;
using Map = std::map<Process, boost::icl::interval_set<double> >; // instead of boost::icl::interval_map<double, std::set<Process> >;
using IVal = Map::mapped_type::interval_type;
Map imap;
imap[{4}] += IVal::right_open(0, 4);
imap[{1}] += IVal::right_open(2, 6);
imap[{4}] += IVal::right_open(4, 9);
imap[{7}] += IVal::closed(8, 8);
//for (auto&& el : imap) { std::cout << el.first << " - " << el.second << std::endl; }
Process key{4};
std::cout << key << " - " << imap[key];
}
This results in:
Process{4} - {[0,9)}
Which is what I think you meant with "in such a way that merging of overlapping intervals is automatically done".
Having Both
Of course you can derive the inverse mappings from the original data-structure:
template <typename IMap>
auto inverted(IMap const& imap) {
std::map<typename IMap::codomain_type::value_type, boost::icl::interval_set<typename IMap::domain_type> > output;
for (auto& el : imap)
for (auto& key: el.second)
output[key] += el.first;
return output;
}
See it Live On Coliru
#include "boost/icl/closed_interval.hpp"
#include <boost/icl/interval_map.hpp>
#include <iostream>
#include <set>
struct Process {
int id;
friend bool operator==(const Process& p, const Process& q) { return p.id == q.id; }
friend bool operator<(const Process& p, const Process& q) { return p.id < q.id; }
};
std::ostream& operator<<(std::ostream& str, const Process& p) {
str << "Process{" << p.id << "}";
return str;
}
template <typename IMap>
auto inverted(IMap const& imap) {
std::map<typename IMap::codomain_type::value_type, boost::icl::interval_set<typename IMap::domain_type> > output;
for (auto& el : imap)
for (auto& key: el.second)
output[key] += el.first;
return output;
}
int main(int, char**) {
using namespace boost::icl;
using IMap = boost::icl::interval_map<double, std::set<Process> >;
using IVal = IMap::interval_type;
IMap imap;
imap.add({ IVal::right_open(0, 4), {Process{ 4 }} });
imap.add({ IVal::right_open(2, 6), {Process{ 1 }} });
imap.add({ IVal::right_open(4, 9), {Process{ 4 }} });
imap.add({ IVal::closed(8, 8), {Process{ 7 }} });
std::cout << imap << "\n\n";
for (auto&& iter : imap) {
std::cout << iter.first << " - " << iter.second << std::endl;
}
Process key{4};
std::cout << key << " - " << inverted(imap)[key] << "\n";
}
More Notes
Querying multiple keys in the domain is directly supported, see a good assortion of pointers here:
split_interval_map usage, efficient find all interval intersecting a point
boost multi_index_container search for records that fall within intervals defined by two fields
You can always construct your own data-structure that affords bi-directional indexes, such as shown e.g.
here boost::multi_index_container, operations on std::set inside container

Iterating on xml file with boost

I am new with boost and xml and I am trying to scan an xml file and save some specific values.
I read this article and my question is: if the xml contains several <sked>, how do I iterate both of them?
maybe
BOOST_FOREACH ()
BOOST_FOREACH () // nested loop
lets say the given xml file as follow (the purpose is to save both IDs):
<?xml version="1.0"? encoding="utf-8"?>
<arrayOfSked>
<sked>
<ID> 1 </ID>
<version>2</version>
<flight>
<carrier>BA</carrier>
<number>4001</number>
<precision>0.1</precision>
</flight>
<flight cancelled="true">
<carrier>BA</carrier>
<number>4002</number>
<precision>0.2</precision>
</flight>
</sked>
<sked>
<ID> 2 </ID>
<version>2</version>
<flight>
<carrier>BA</carrier>
<number>4001</number>
<precision>0.1</precision>
</flight>
<flight cancelled="true">
<carrier>BA</carrier>
<number>4002</number>
<precision>0.2</precision>
</flight>
</sked>
</arrayOfSked>
Taking some inspiration from this older answer of mine (boost::ptree find? or how to access deep arrays? C++), write a function to visit the tree nodes:
template <typename Tree, typename Out>
Out enumerate_nodes(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
auto name = path.reduce();
for (auto& child : pt)
{
if (child.first == name)
*out++ = child.second;
}
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_nodes(child.second, path, out);
}
}
}
return out;
}
Now you can simply read the file:
using boost::property_tree::ptree;
ptree pt;
{
std::ifstream ifs("input.txt");
read_xml(ifs, pt);
}
And collect the flights:
std::vector<std::reference_wrapper<ptree const> > flights;
enumerate_nodes<ptree>(pt, "arrayOfSked.sked.flight", back_inserter(flights));
for (ptree const& flight : flights) {
std::cout << "Canceled:\t" << (flight.get("<xmlattr>.cancelled", false)?"yes":"no") << "\n";
std::cout << "Carrier:\t" << flight.get("carrier", "?") << "\n";
std::cout << "Number:\t" << flight.get("number", 0) << "\n";
std::cout << "Precision:\t" << flight.get("precision", 0.0) << "\n";
std::cout << "------------------------------\n";
}
Which prints:
Canceled: no
Carrier: BA
Number: 4001
Precision: 0.1
------------------------------
Canceled: yes
Carrier: BA
Number: 4002
Precision: 0.2
------------------------------
Canceled: no
Carrier: BA
Number: 4001
Precision: 0.1
------------------------------
Canceled: yes
Carrier: BA
Number: 4002
Precision: 0.2
------------------------------
Full Demo Online
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
template <typename Tree, typename Out>
Out enumerate_nodes(Tree const& pt, typename Tree::path_type path, Out out) {
if (path.empty())
return out;
if (path.single()) {
auto name = path.reduce();
for (auto& child : pt)
{
if (child.first == name)
*out++ = child.second;
}
} else {
auto head = path.reduce();
for (auto& child : pt) {
if (head == "*" || child.first == head) {
out = enumerate_nodes(child.second, path, out);
}
}
}
return out;
}
int main() {
using boost::property_tree::ptree;
ptree pt;
{
std::ifstream ifs("input.txt");
read_xml(ifs, pt);
}
std::vector<std::reference_wrapper<ptree const> > flights;
enumerate_nodes<ptree>(pt, "arrayOfSked.sked.flight", back_inserter(flights));
for (ptree const& flight : flights) {
std::cout << "Canceled:\t" << (flight.get("<xmlattr>.cancelled", false)?"yes":"no") << "\n";
std::cout << "Carrier:\t" << flight.get("carrier", "?") << "\n";
std::cout << "Number:\t" << flight.get("number", 0) << "\n";
std::cout << "Precision:\t" << flight.get("precision", 0.0) << "\n";
std::cout << "------------------------------\n";
}
}

Finding duplicates in JSON file after parsing with Boost

How can I find duplicates in a JSON file after parsing it out like the code below? I want to count the number of duplicates in the data where a duplicate would have the first name, last name, and email address all match.
The JSON file is rather huge, so I won't copy and paste it here. But here is a snippet of it:
[
{
"firstName":"Cletus",
"lastName":"Defosses",
"emailAddress":"ea4ad81f-4111-4d8d-8738-ecf857bba992.Defosses#somedomain.org"
},
{
"firstName":"Sherron",
"lastName":"Siverd",
"emailAddress":"51c985c5-381d-4d0e-b5ee-83005f39ce17.Siverd#somedomain.org"
},
{
"firstName":"Garry",
"lastName":"Eirls",
"emailAddress":"cc43c2da-d12c-467f-9318-beb3379f6509.Eirls#somedomain.org"
}]
This is the main.cpp file:
#include <iostream>
#include <string>
#include "Customer.h"
#include "boost\property_tree\ptree.hpp"
#include "boost\property_tree\json_parser.hpp"
#include "boost\foreach.hpp"
using namespace std;
int main()
{
int numOfCustomers;
// parse the JSON file
boost::property_tree::ptree file;
boost::property_tree::read_json("customers.json", file);
cout << "Reading file..." << endl;
numOfCustomers = file.size();
// iterate over each top level entry
BOOST_FOREACH(boost::property_tree::ptree::value_type const& rowPair, file.get_child(""))
{
// rowPair.first == "" and rowPair.second is the subtree with names and emails
// iterate over rows and columns
BOOST_FOREACH(boost::property_tree::ptree::value_type const& itemPair, rowPair.second)
{
// e.g. itemPair.first == "firstName: " or "lastName: "
cout << itemPair.first << ": ";
// e.g. itemPair.second is the actual names and emails
cout << itemPair.second.get_value<std::string>() << endl;
}
cout << endl;
}
cout << endl;
return 0;
}
The Customer class is just a generic class.
class Customer
{
private:
std::string m_firstNme;
std::string m_lastName;
std::string m_emailAddress;
public:
std::string getFirstName();
void setFirstName(std::string firstName);
std::string getLastName();
void setLastName(std::string lastName);
std::string getEmailAddress();
void setEmailAddress(std::string emailAddress);
};
You'd typically insert the customer objects/keys into a std::set or std::map and define a total ordering that spots the duplicates on insertion.
Defining the key function and comparator object:
boost::tuple<string const&, string const&, string const&> key_of(Customer const& c) {
return boost::tie(c.getFirstName(), c.getLastName(), c.getEmailAddress());
}
struct by_key {
bool operator()(Customer const& a, Customer const& b) const {
return key_of(a) < key_of(b);
}
};
Now you can simply insert the objects in a set<Customer, by_key>:
set<Customer, by_key> unique;
// iterate over each top level array
BOOST_FOREACH(boost::property_tree::ptree::value_type const& rowPair, file.get_child(""))
{
Customer current;
current.setFirstName ( rowPair.second.get ( "firstName", "?" ) ) ;
current.setLastName ( rowPair.second.get ( "lastName", "?" ) ) ;
current.setEmailAddress ( rowPair.second.get ( "emailAddress", "?" ) ) ;
if (unique.insert(current).second)
cout << current << "\n";
else
cout << "(duplicate skipped)\n";
}
Full Demo
I've duplicated 1 entry in your sample JSON, and you can see it live
Live On Coliru
#include <iostream>
#include <string>
#include <set>
#include "Customer.h"
#include <boost/property_tree/ptree.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/foreach.hpp>
#include <boost/tuple/tuple_comparison.hpp>
using namespace std;
namespace {
boost::tuple<string const&, string const&, string const&> key_of(Customer const& c) {
return boost::tie(c.getFirstName(), c.getLastName(), c.getEmailAddress());
}
struct by_key {
bool operator()(Customer const& a, Customer const& b) const {
return key_of(a) < key_of(b);
}
};
inline ostream& operator<<(ostream& os, Customer const& c) {
return os << "{ '"
<< c.getFirstName() << "', '"
<< c.getLastName() << "', '"
<< c.getEmailAddress() << " }";
}
}
int main()
{
// parse the JSON file
boost::property_tree::ptree file;
boost::property_tree::read_json("customers.json", file);
cout << "Reading file..." << endl;
set<Customer, by_key> unique;
// iterate over each top level array
BOOST_FOREACH(boost::property_tree::ptree::value_type const& rowPair, file.get_child(""))
{
Customer current;
current.setFirstName ( rowPair.second.get ( "firstName", "?" ) ) ;
current.setLastName ( rowPair.second.get ( "lastName", "?" ) ) ;
current.setEmailAddress ( rowPair.second.get ( "emailAddress", "?" ) ) ;
if (unique.insert(current).second)
cout << current << "\n";
else
cout << "(duplicate skipped)\n";
}
cout << "\n" << (file.size() - unique.size()) << " duplicates were found\n";
}
Prints:
Reading file...
{ 'Sherron', 'Siverd', '51c985c5-381d-4d0e-b5ee-83005f39ce17.Siverd#somedomain.org }
{ 'Cletus', 'Defosses', 'ea4ad81f-4111-4d8d-8738-ecf857bba992.Defosses#somedomain.org }
(duplicate skipped)
{ 'Garry', 'Eirls', 'cc43c2da-d12c-467f-9318-beb3379f6509.Eirls#somedomain.org }
1 duplicates were found
NOTE I've adjusted the getters to be less wasteful by returning const&:
std::string const& getFirstName() const { return m_firstName; }
std::string const& getLastName() const { return m_lastName; }
std::string const& getEmailAddress() const { return m_emailAddress; }
BONUS
Here's the equivalent program in 26 lines of c++14 code:
Live On Coliru