微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

使用 BOOST 库以 BGL 图的形式存储嵌套的 XML 结构 顶点边缘

如何解决使用 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 图。

非常感谢所有帮助。 谢谢!!

解决方法

好的,因为没有很多其他 流量,让我们这样做。

首先,让我们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";
}

打印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]

作为奖励,让我们包含一个 DOT 图输出:

enter image description here

完整的现场演示

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_,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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?