基于Boost BGL将嵌套XML存储为无向图 以类替代struct实现
实现思路
核心是将原有零散的结构化存储逻辑重构为类封装的BGL无向图存储,严格匹配提出的XML到图的映射规则:
- 顶点层:所有
<car>、<plane>元素作为图的独立顶点,顶点属性封装对应元素的全部XML字段,通过类型字段区分汽车、飞机两类节点,避免为不同车辆类型定义重复的结构体。 - 边层:所有
<spec>、<additions>、<utilities>子元素作为图的边载体,边属性封装对应子元素的全部XML字段,完全替代原代码中顶点内部嵌套存储子节点列表的冗余结构。 - 图结构:采用BGL原生无向图容器,将顶点、边属性直接绑定到图的内置属性系统,无需额外维护外挂的属性列表。
核心类设计(替代原有零散struct)
用类封装所有数据结构,替代原有的Car、Spec结构体,兼顾类型可扩展性:
顶点属性类 VehicleVertex
class VehicleVertex { private: std::string node_type; // 标记节点类型:"car"/"plane" std::string name; // 节点的name属性 std::string extra_info; // 存储非共有属性:car的weight、plane的id public: // 构造函数 VehicleVertex(std::string t, std::string n, std::string info) : node_type(std::move(t)), name(std::move(n)), extra_info(std::move(info)) {} // 友元打印函数 friend std::ostream& operator<<(std::ostream& os, const VehicleVertex& v) { os << "顶点类型: " << v.node_type << ", 名称: " << v.name << ", 附加属性: " << v.extra_info; return os; } };
边属性类 ChildNodeEdge
class ChildNodeEdge { private: std::string edge_type; // 标记边来源子节点类型:"spec"/"additions"/"utilities" std::map<std::string, std::string> attrs; // 存储子节点的全部XML属性 public: // 构造函数 ChildNodeEdge(std::string t, std::map<std::string, std::string> a) : edge_type(std::move(t)), attrs(std::move(a)) {} // 友元打印函数 friend std::ostream& operator<<(std::ostream& os, const ChildNodeEdge& e) { os << "边类型: " << e.edge_type << ", 属性: {"; for (auto it = e.attrs.begin(); it != e.attrs.end(); ++it) { if (it != e.attrs.begin()) os << ", "; os << it->first << "=" << it->second; } os << "}"; return os; } };
BGL无向图定义
基于上述两个类,直接定义符合要求的无向图类型:
#include <boost/graph/adjacency_list.hpp> // 无向图配置:顶点存储VehicleVertex,边存储ChildNodeEdge using VehicleGraph = boost::adjacency_list< boost::vecS, // 边容器:动态数组 boost::vecS, // 顶点容器:动态数组 boost::undirectedS, // 无向图标记 VehicleVertex, // 绑定顶点属性 ChildNodeEdge // 绑定边属性 >;
XML到图的解析流程
严格匹配映射规则,解析过程分为三步:
- 初始化图的固定功能顶点:提前创建3个对应子节点类型的中心顶点(分别关联
spec、additions、utilities类型),用于和车辆类顶点连边,满足无向图“边必须连接两个顶点”的结构要求。 - 遍历所有车辆类顶点:
- 遍历
root.type.cars下的所有<car>节点,解析name、weight属性,创建VehicleVertex加入图中 - 遍历
root.type.planes下的所有<plane>节点,解析name、id属性,创建VehicleVertex加入图中
- 遍历
- 遍历所有子节点生成边:对每个车辆类节点,遍历其下的子节点,收集子节点的全部XML属性,构造
ChildNodeEdge对象,从当前车辆顶点连一条边到对应的功能中心顶点。
完整可运行代码示例
#include <boost/property_tree/xml_parser.hpp> #include <boost/graph/adjacency_list.hpp> #include <boost/range/adaptor/indexed.hpp> #include <fstream> #include <map> #include <iostream> using boost::property_tree::ptree; // 顶点属性类 class VehicleVertex { private: std::string node_type; std::string name; std::string extra_info; public: VehicleVertex(std::string t, std::string n, std::string info) : node_type(std::move(t)), name(std::move(n)), extra_info(std::move(info)) {} friend std::ostream& operator<<(std::ostream& os, const VehicleVertex& v) { os << "顶点[类型:" << v.node_type << ", 名称:" << v.name << ", 附加:" << v.extra_info << "]"; return os; } }; // 边属性类 class ChildNodeEdge { private: std::string edge_type; std::map<std::string, std::string> attrs; public: ChildNodeEdge(std::string t, std::map<std::string, std::string> a) : edge_type(std::move(t)), attrs(std::move(a)) {} friend std::ostream& operator<<(std::ostream& os, const ChildNodeEdge& e) { os << "边[类型:" << e.edge_type << ", 属性:{"; for (auto it = e.attrs.begin(); it != e.attrs.end(); ++it) { if (it != e.attrs.begin()) os << ","; os << it->first << "=" << it->second; } os << "}]"; return os; } }; // 图类型定义 using VehicleGraph = boost::adjacency_list< boost::vecS, boost::vecS, boost::undirectedS, VehicleVertex, ChildNodeEdge >; int main() { // 解析XML ptree pt; std::ifstream ifs("input.xml"); read_xml(ifs, pt); VehicleGraph g; std::map<std::string, VehicleGraph::vertex_descriptor> func_vertex_map; // 初始化功能中心顶点 func_vertex_map["spec"] = boost::add_vertex( VehicleVertex("feature", "spec", "中心节点"), g ); func_vertex_map["additions"] = boost::add_vertex( VehicleVertex("feature", "additions", "中心节点"), g ); func_vertex_map["utilities"] = boost::add_vertex( VehicleVertex("feature", "utilities", "中心节点"), g ); // 解析car节点为顶点 for (auto& [key, node] : pt.get_child("root.type.cars")) { if (key != "car") continue; std::string name = node.get<std::string>("<xmlattr>.name"); std::string weight = node.get<std::string>("<xmlattr>.weight"); auto vd = boost::add_vertex(VehicleVertex("car", name, "weight=" + weight), g); // 遍历该car的子节点生成边 for (auto& [child_key, child_node] : node) { if (!func_vertex_map.count(child_key)) continue; // 收集子节点的所有属性 std::map<std::string, std::string> attrs; for (auto& [attr_key, attr_val] : child_node.get_child("<xmlattr>")) { attrs[attr_key] = attr_val.data(); } // 添加边 boost::add_edge(vd, func_vertex_map[child_key], ChildNodeEdge(child_key, attrs), g ); } } // 解析plane节点为顶点 for (auto& [key, node] : pt.get_child("root.type.planes")) { if (key != "plane") continue; std::string name = node.get<std::string>("<xmlattr>.name"); std::string id = node.get<std::string>("<xmlattr>.id"); auto vd = boost::add_vertex(VehicleVertex("plane", name, "id=" + id), g); // 遍历该plane的子节点生成边 for (auto& [child_key, child_node] : node) { if (!func_vertex_map.count(child_key)) continue; std::map<std::string, std::string> attrs; for (auto& [attr_key, attr_val] : child_node.get_child("<xmlattr>")) { attrs[attr_key] = attr_val.data(); } boost::add_edge(vd, func_vertex_map[child_key], ChildNodeEdge(child_key, attrs), g ); } } // 遍历打印图的内容 std::cout << "=== 图顶点列表 ===" << std::endl; for (auto v : boost::vertices(g)) { std::cout << "顶点ID " << v << ": " << g[v] << std::endl; } std::cout << "\n=== 图边列表 ===" << std::endl; for (auto [ed, end] = boost::edges(g); ed != end; ++ed) { auto u = boost::source(*ed, g); auto v = boost::target(*ed, g); std::cout << g[u] << " <-> " << g[v] << " | " << g[*ed] << std::endl; } return 0; }
代码适配说明:
- 完全移除了原代码中硬编码的结构体定义,所有数据存储通过类实现
- 保留了原有的XML解析逻辑,没有修改Boost.PropertyTree的解析接口
- 图结构为标准BGL无向图,所有属性直接绑定到图内部存储,支持后续直接调用BGL的图算法接口
内容的提问来源于stack exchange,提问作者user12061536
相关产品推荐
相关产品推荐

