Okay, because there's not a lot of other boost traffic, let's do this.
First, let's fix the input so that it is actually XML:
<?xml version="1.0" encoding="utf-8"?>
<root>
<type>
<cars>
<car name="Garfield" weight="4Kg">
<spec serial_="e_54" source ="petrol" mileage="56"/>
<spec serial_="e_52" source="diesel" mileage="52"/>
<spec serial_="m_22" source="electric" mileage="51"/>
<additions source="steam" convert="153 0 1 0"/>
</car>
<car name="Awesome" weight="3Kg">
<spec serial_="t_54" source="petrol" mileage="16"/>
<spec serial_="t_52" source="wind" mileage="62"/>
<spec serial_="t_22" source="electric" mileage="81"/>
<additions source="water" convert="123 1 1 0"/>
</car>
</cars>
<planes>
<plane id="231" name ="abc">
<utilities serial_="e456" part="567"/>
</plane>
</planes>
</type>
</root>
Now, let's add parsing for the planes:
struct Plane {
Id id;
std::string name;
struct Utilities {
std::string serial_, part;
};
Utilities utilities;
};
static bool parse(ptree const& node, Plane::Utilities& into) {
into.serial_ = node.get<std::string>("<xmlattr>.serial_");
into.part = node.get<std::string>("<xmlattr>.part");
return true;
}
static bool parse(ptree const& node, Plane& into) {
into.id = node.get<Id>("<xmlattr>.id");
into.name = node.get<std::string>("<xmlattr>.name");
if (auto child = node.get_child_optional("utilities")) {
return parse(*child, into.utilities);
}
return true;
}
So far, nothing new. Well, we might add the additions
to cars:
struct Additions {
std::string source;
std::vector<double> convert;
};
Additions additions;
Which you can parse using something like
static bool parse(ptree const& node, Car::Additions& into) {
into.source = node.get<std::string>("<xmlattr>.source");
auto values = node.get<std::string>("<xmlattr>.convert");
if (!x3::parse(
values.begin(), values.end(),
x3::skip(x3::space) [*x3::double_],
into.convert))
return false;
return true;
}
Making It A Graph
Instead of "magically" not having the structs but still having the data (how?) you would probably want to attach the structs to your graph:
using VertexBundle = boost::variant<Car, Plane>;
using EdgeBundle = std::string;
using Graph = boost::adjacency_list<
boost::vecS, boost::vecS, boost::directedS,
VertexBundle, EdgeBundle>;
Vertices
There, now let's parse those vertices from the XML:
Graph g;
auto parse_vehicles = [&pt,&g]<typename Type>(auto path, auto key) {
for (auto& [k, node] : pt.get_child(path)) {
if (k == key) {
Type vehicle;
parse(node, vehicle);
add_vertex(vehicle, g);
}
}
};
parse_vehicles.operator()<Car>("root.type.cars", "car");
parse_vehicles.operator()<Plane>("root.type.planes", "plane");
Note how nice and generic that parse loop already was.
Edges
There's nothing in your question indicating how we get any edge information, so let's just make something up for demo purposes:
// TODO add edges, but there's no information on how to
add_edge(vertex(0, g), vertex(2, g), "This demo edge has no properties", g);
add_edge(vertex(2, g), vertex(1, g), "One more", g);
Now you can print the whole thing as before:
for (Vertex v : boost::make_iterator_range(vertices(g))) {
std::cout << g[v] << "\n";
}
Printing Live On Coliru
Name: Garfield, Weight: 4Kg
-- [e_54; petrol; 56]
-- [e_52; diesel; 52]
-- [m_22; electric; 51]
-- additions [steam; 153/0/1/0]
Name: Awesome, Weight: 3Kg
-- [t_54; petrol; 16]
-- [t_52; wind; 62]
-- [t_22; electric; 81]
-- additions [water; 123/1/1/0]
Id: 231, Name: abc
-- utilities [e456; 567]
As a bonus let's include a DOT graph output:

Full Live Demo
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <boost/variant.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/spirit/home/x3.hpp>
namespace x3 = boost::spirit::x3;
using boost::property_tree::ptree;
#include <iostream>
using Id = std::uint32_t;
struct Car {
std::string name, weight;
struct Spec {
std::string serial_, source;
double mileage;
};
struct Additions {
std::string source;
std::vector<double> convert;
};
std::vector<Spec> specs;
Additions additions;
};
struct Plane {
Id id;
std::string name;
struct Utilities {
std::string serial_, part;
};
Utilities utilities;
};
static bool parse(ptree const& node, Car::Additions& into) {
into.source = node.get<std::string>("<xmlattr>.source");
auto values = node.get<std::string>("<xmlattr>.convert");
if (!x3::parse(
values.begin(), values.end(),
x3::skip(x3::space) [*x3::double_],
into.convert))
return false;
return true;
}
static bool parse(ptree const& node, Car::Spec& into) {
into.serial_ = node.get<std::string>("<xmlattr>.serial_");
into.source = node.get<std::string>("<xmlattr>.source");
into.mileage = node.get<double>("<xmlattr>.mileage");
into.mileage = node.get<double>("<xmlattr>.mileage");
return true;
}
static bool parse(ptree const& node, Car& into) {
into.name = node.get<std::string>("<xmlattr>.name");
into.weight = node.get<std::string>("<xmlattr>.weight");
for (auto& [name, child] : node) {
if (name == "spec") {
into.specs.emplace_back();
if (!parse(child, into.specs.back())) {
return false;
}
}
}
if (auto child = node.get_child_optional("additions")) {
return parse(*child, into.additions);
}
return true;
}
static bool parse(ptree const& node, Plane::Utilities& into) {
into.serial_ = node.get<std::string>("<xmlattr>.serial_");
into.part = node.get<std::string>("<xmlattr>.part");
return true;
}
static bool parse(ptree const& node, Plane& into) {
into.id = node.get<Id>("<xmlattr>.id");
into.name = node.get<std::string>("<xmlattr>.name");
if (auto child = node.get_child_optional("utilities")) {
return parse(*child, into.utilities);
}
return true;
}
static std::ostream& operator<<(std::ostream& os, Car const& car) {
os << "Name: " << car.name << ", Weight: " << car.weight;
for (auto& spec : car.specs) {
os << "\n -- [" << spec.serial_ << "; " << spec.source << "; "
<< spec.mileage << "]";
}
auto& a = car.additions;
if (!(a.source.empty() && a.convert.empty())) {
os << "\n -- additions [" << a.source << ";";
auto sep = ' ';
for (auto d : a.convert) {
os << std::exchange(sep, '/') << d;
}
os << "]";
}
return os;
}
static std::ostream& operator<<(std::ostream& os, Plane const& plane) {
os << "Id: " << plane.id << ", Name: " << plane.name;
auto& u = plane.utilities;
if (!(u.serial_.empty() && u.part.empty())) {
os << "\n -- utilities [" << u.serial_ << "; " << u.part << "]";
}
return os;
}
using VertexBundle = boost::variant<Car, Plane>;
using EdgeBundle = std::string;
using Graph = boost::adjacency_list<
boost::vecS, boost::vecS, boost::directedS,
VertexBundle, EdgeBundle>;
using Vertex = Graph::vertex_descriptor;
using Edge = Graph::edge_descriptor;
int main()
{
boost::property_tree::ptree pt;
{
std::ifstream ifs("input.xml");
read_xml(ifs, pt);
}
Graph g;
auto parse_vehicles = [&pt,&g]<typename Type>(auto path, auto key) {
for (auto& [k, node] : pt.get_child(path)) {
if (k == key) {
Type vehicle;
parse(node, vehicle);
add_vertex(vehicle, g);
}
}
};
parse_vehicles.operator()<Car>("root.type.cars", "car");
parse_vehicles.operator()<Plane>("root.type.planes", "plane");
// TODO add edges, but there's no information on how to
add_edge(vertex(0, g), vertex(2, g), "This demo edge has no properties", g);
add_edge(vertex(2, g), vertex(1, g), "One more", g);
for (Vertex v : boost::make_iterator_range(vertices(g))) {
std::cout << g[v] << "\n";
}
{
auto vindex = get(boost::vertex_index, g);
auto calc_color = [&](Vertex v) { return g[v].which()? "red":"blue"; };
auto calc_label = [&](Vertex v) {
// multiline Mrecord label formatting
auto txt = boost::lexical_cast<std::string>(g[v]);
boost::algorithm::replace_all(txt, "\n --", "|");
return "{" + txt + "}";
};
boost::dynamic_properties dp;
dp.property("node_id", vindex);
dp.property("label", boost::make_transform_value_property_map(calc_label, vindex));
dp.property("fontcolor", boost::make_transform_value_property_map(calc_color, vindex));
dp.property("style", boost::make_static_property_map<Vertex>(std::string("filled")));
dp.property("label", get(boost::edge_bundle, g));
auto pw = boost::dynamic_vertex_properties_writer { dp, "node_id" };
using Map = std::map<std::string, std::string>;
auto gpw = boost::make_graph_attributes_writer(Map{}, Map {{"shape", "Mrecord"}}, Map{});
std::ofstream ofs("graph.dot");
write_graphviz(ofs, g, pw, pw, gpw);
}
}
Prints the output shown above, as well as the following graph.dot
:
digraph G {
node [
shape=Mrecord];
0 [fontcolor=blue, label="{Name: Garfield, Weight: 4Kg| [e_54; petrol; 56]| [e_52; diesel; 52]| [m_22; electric; 51]| additions [steam; 153/0/1/0]}", style=filled];
1 [fontcolor=blue, label="{Name: Awesome, Weight: 3Kg| [t_54; petrol; 16]| [t_52; wind; 62]| [t_22; electric; 81]| additions [water; 123/1/1/0]}", style=filled];
2 [fontcolor=red, label="{Id: 231, Name: abc| utilities [e456; 567]}", style=filled];
0->2 [label="This demo edge has no properties"];
2->1 [label="One more"];
}