如何解决使用 BOOST 库以 BGL 图的形式存储嵌套的 XML 结构 顶点边缘
我正在尝试存储嵌套的 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>
</type>
</root>
来自 boost 库无向图。根据 XML,我希望使每个“汽车”和“飞机”成为一个节点,同时将属性存储为节点的数据成员。接下来,它们的子节点,即“spec”、“additions”和“utilities”将与它们的属性一起以边的形式存储。
代码结构如下:
#include <boost/property_tree/xml_parser.hpp>
using boost::property_tree::ptree;
#include <iostream>
struct Car {
std::string name,weight;
struct Spec {
std::string serial_,source;
double mileage;
};
std::vector<Spec> specs;
};
//Note: struct identical to struct car have been devised to store planes and additions
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");
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;
}
}
}
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 << "]";
}
return os;
}
int main()
{
boost::property_tree::ptree pt;
{
std::ifstream ifs("input.xml");
read_xml(ifs,pt);
}
for (auto& [key,node] : pt.get_child("root.type.cars")) {
if ("car" == key) {
Car car;
parse(node,car);
std::cout << car << "\n";
}
}
}
我希望摆脱结构,并在适当的位置使用一个类来代替存储并形成所描绘的 BGL 图。
非常感谢所有帮助。 谢谢!!
解决方法
好的,因为没有很多其他 boost 流量,让我们这样做。
首先,让我们fix the input 使其实际上是 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>
现在,让我们为平面添加解析:
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;
}
到目前为止,没有什么新鲜事。好吧,我们可以将 additions
添加到汽车中:
struct Additions {
std::string source;
std::vector<double> convert;
};
Additions additions;
你可以使用类似的东西来解析
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;
}
制作图表
与其“神奇地”没有结构但仍然拥有数据(如何?),您可能希望将结构附加到您的图形中:
using VertexBundle = boost::variant<Car,Plane>;
using EdgeBundle = std::string;
using Graph = boost::adjacency_list<
boost::vecS,boost::vecS,boost::directedS,VertexBundle,EdgeBundle>;
顶点
好了,现在让我们从 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");
注意解析循环已经是多么的好和通用。
边缘
您的问题中没有任何内容表明我们如何获得任何边缘信息,所以让我们为演示目的而做一些补充:
// TODO add edges,but there's no information on how to
add_edge(vertex(0,g),vertex(2,"This demo edge has no properties",g);
add_edge(vertex(2,vertex(1,"One more",g);
现在您可以像以前一样打印整个内容:
for (Vertex v : boost::make_iterator_range(vertices(g))) {
std::cout << g[v] << "\n";
}
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]
作为奖励,让我们包含一个 DOT 图输出:
完整的现场演示
#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_,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,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,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,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);
add_edge(vertex(2,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,gpw);
}
}
打印上面显示的输出,以及下面的 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"];
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。