提问者:小点点

pugiXML:如何在XML树中进行深度优先遍历


我有以下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实现深度优先的树遍历,有没有人可以帮助我或者给我指明正确的方向?提前道谢。


共1个答案

匿名用户

在assimp中,我编写了一个XML-NodeIterator,它将遍历整个树。您可以在这里找到它:Assimp-XmlNodeIterator