我有以下XML文件,希望使用PugiXML库将其解析为C++:
<?xml version="1.0" encoding="UTF-8"?>
<graph id="g" edgedefault="undirected">
<vertex id="1">
<sensornode name="ESP32" room="1"/>
</vertex>
<vertex id="2">
<sensornode name="Arduino" room="2"/>
</vertex>
<vertex id="3">
<sensornode name="STM32" room="3"/>
</vertex>
<edge id="1" vertex1="1" vertex2="2" />
<edge id="2" vertex1="1" vertex2="3" />
</graph>
我用C++创建了一个图结构。现在的任务是从XML文件到C++图结构。这是我目前为止最好的尝试:
int main()
{
// String definitions: these are necessary for the string.compare() method.
std::string graph_string ("graph");
std::string edge_string ("edge");
std::string vertex_string ("vertex");
std::string sensornode_string("sensornode");
// Pugi::xml initialization
pugi::xml_document file;
file.load_file("../graph.graphml");
// Create an empty graph. This is needed due to the if statements that have nothing to do with 'if graph_string'.
// For example, the 'if edge_string' does not know about graph g
Graph g = Graph();
// Make 'root' the root of the XML tree.
pugi::xml_node root = file.child("graph"); // 'root' contains a list of graphs. But in reality, there is only one graph. Therefore, let's make this one graph the root.
for(pugi::xml_node xml_node = root.first_child(); xml_node; xml_node = root.next_sibling())
{
std::cout << "XML node: " << xml_node << std::endl;
if(graph_string.compare(xml_node.name())) {
g = createGraphFromXML(xml_node);
std::cout << "Graph g overwritten" << std::endl; // This should be the first output in the console!
}
else if(edge_string.compare(xml_node.name())) {
Edge e = Edge();
e = createEdgeFromXML(xml_node, &g);
std::cout << "Edge id: " << e.getId() << std::endl;
// TODO: add e to g
}
else if (vertex_string.compare(xml_node.name()) == 0) {
Vertex v = Vertex();
v = createVertexFromXML(xml_node);
std::cout << "Vertex id: " << v.getId() << std::endl;
// TODO: add v to g
}
else if (sensornode_string.compare(xml_node.name()) == 0) {
std::cout << "Sensornode: skip to next iteration" << std::endl;
continue; // Skip this since it is already handled in 'createVertexFromXML'.
}
else {
std::cout << "ERROR KUT" << std::endl;
throw std::invalid_argument("TODO: zet hier iets nuttigs");
}
std::cout << std::endl;
}
std::cout << std::endl;
std::cin.get();
return 0;
}
double randomDouble(double lower, double upper)
{
/*
* This function generates a random double value that
* is between lower and upper bounds.
* Source: https://stackoverflow.com/a/9324796
*/
std::uniform_real_distribution<double> unif(lower, upper);
std::default_random_engine re;
return unif(re);
}
Graph createGraphFromXML(pugi::xml_node xml_node) {
/*
* This function takes the XML node as input. It then processes
* this information, taking the XML attributes into account.
* Finally, it returns the Graph object.
*
* Note: to keep it as simplea as possible, we will not process the extra graph information (e.g. if directed).
*/
// Create and return the graph
Graph g = Graph();
return g;
}
Edge createEdgeFromXML(pugi::xml_node xml_node, Graph *g) {
/*
* This function takes the XML node as input. It then processes
* this information, taking the XML attributes into account.
* Finally, it returns the Edge object.
*/
// Note: you have to append this to the graph's edge list!
// String definitions: these are necessary for the string.compare() method.
std::string id_string ("id");
std::string vertex1_string ("vertex1");
std::string vertex2_string ("vertex2");
// Create a new edge e
Edge e = Edge(); // Use the default constructor that has no input parameters. This way, we can add the id, vertex1 and vertex2 later.
// // Iterate over the attributes of 'xml_node'.
for(pugi::xml_attribute attr = xml_node.first_attribute(); attr; attr = attr.next_attribute())
{
std::cout << " " << attr.name() << "=" << attr.value() << std::endl; // Print the attribute name and value.
if(id_string.compare(attr.name()) == 0) {
//const char_t *id = attr.value();
unsigned short id = (unsigned short) *attr.value();
e.addId(id);
}
else if(vertex1_string.compare(attr.name()) == 0) {
unsigned short id_vertex1 = (unsigned short) *attr.value();
// Iterate over the list of vertices to get a pointer to the correct vertex.
std::list<Vertex*> vertexList = g->getVertices(); // Get the list of vertices
std::list<Vertex*>::iterator iter; // Create the iterator
for (iter = vertexList.begin(); iter != vertexList.end(); ++iter) {
if((**iter).getId() == id_vertex1) {
e.addVertex1(*iter); // *iter points to the first vertex.
}
}
}
else if(vertex2_string.compare(attr.name()) == 0) {
unsigned short id_vertex2 = (unsigned short) *attr.value();
// Iterate over the list of vertices to get a pointer to the correct vertex.
std::list<Vertex*> vertexList = g->getVertices(); // Get the list of vertices
std::list<Vertex*>::iterator iter; // Create the iterator
for (iter = vertexList.begin(); iter != vertexList.end(); ++iter) {
if((**iter).getId() == id_vertex2) {
e.addVertex2(*iter); // *iter points to the second vertex.
}
}
}
}
// Return the edge e
return e; // TODO: make this a pointer?
}
Vertex createVertexFromXML(pugi::xml_node xml_node) { // , Graph *g
// String definitions: these are necessary for the string.compare() method.
std::string id_string ("id");
std::string name_string ("name");
std::string room_string ("room");
// Create an empty Vertex and an empty SensorNode. These will be filled later on.
Vertex v = Vertex();
SensorNode sensorNode;
sensorNode.temperature = randomDouble(18.0, 24.0); // degrees Celsius
sensorNode.humidity = randomDouble(0.3, 0.7); // %, TODO: optimize this.
sensorNode.co2 = randomDouble(400, 800); // ppm, TODO: optimize this.
// Iterate over the attributes of 'xml_node'.
for(pugi::xml_attribute attr = xml_node.first_attribute(); attr; attr = attr.next_attribute()) {
std::cout << " " << attr.name() << "=" << attr.value() << std::endl; // Print the attribute name and value.
if (id_string.compare(attr.name()) == 0) {
//const char_t *id = attr.value();
unsigned short id = (unsigned short) *attr.value();
v.addId(id);
}
}
// Get the XML child. For a vertex, this is <sensornode/>.
pugi::xml_node xml_child = xml_node.child("sensornode");
// Iterate over the attributes of 'xml_child'
for(pugi::xml_attribute attr = xml_child.first_attribute(); attr; attr = attr.next_attribute()) {
std::cout << " " << attr.name() << "=" << attr.value() << std::endl; // Print the attribute name and value.
if (name_string.compare(attr.name()) == 0) {
//const char_t *id = attr.value();
sensorNode.name = (std::string) reinterpret_cast<const char *>(*attr.value()); // TODO: what's this???
}
else if (room_string.compare(attr.name()) == 0) {
sensorNode.room = (std::string) reinterpret_cast<const char *>(*attr.value());
}
}
// Add sensorNode to vertex v
v.addSensorNode(&sensorNode);
// Return the vertex v
return v;
}
这段代码的问题在于它只处理XML节点“graph”。并不是所有的孩子都能接受。我发现一个可能的解决方案是使用深度优先遍历XML树。您可以在这里找到相应的文档(查找“Simple Walker”示例)。现在我被困住了,我不知道如何实现“简单的walker”结构,以一种简单易懂的方式处理XML树。
关于如何使用pugixml实现深度优先的树遍历,有没有人可以帮助我或者给我指明正确的方向?提前道谢。
在assimp中,我编写了一个XML-NodeIterator,它将遍历整个树。您可以在这里找到它:Assimp-XmlNodeIterator