0

I'm using the pugixml library, and i try to add some content (nodes) with the fillWeek() function after sorting existing nodes in my xml file, but the problem is, when i call the sort() function, nothing happens, the _weekNode is not updated. Here is a minimal and reproducible example code :

#include <pugixml.hpp>
#include <cstring>
#include <vector>
#include <algorithm>
#include <iostream>

pugi::xml_document _doc;
pugi::xml_node _rootNode;
pugi::xml_node _weekNode;

bool compareNodesByAttribute(const pugi::xml_node &node1, const pugi::xml_node &node2)
{
    return std::strcmp(node1.attribute("From").value(), node2.attribute("From").value()) < 0;
}

void sort()
{
    std::vector<pugi::xml_node> nodesToSort;

    for (pugi::xml_node node = _rootNode.first_child(); node; node = node.next_sibling())
    {
        nodesToSort.push_back(node);
    }

    std::sort(nodesToSort.begin(), nodesToSort.end(), compareNodesByAttribute);

    for (const pugi::xml_node& node : nodesToSort)
    {
        _rootNode.remove_child(node);
    }

    for (pugi::xml_node node : nodesToSort)
    {
        _rootNode.append_copy(node);
    }

}

void loadXML()
{
    pugi::xml_parse_result result = _doc.load_file("Test.xml");

    if (result.status != pugi::status_ok)
    {
        pugi::xml_node declNode = _doc.prepend_child(pugi::node_declaration);
        declNode.append_attribute("version") = "1.0";
        declNode.append_attribute("encoding") = "UTF-8";
        declNode.append_attribute("standalone") = "yes";

        _rootNode = _doc.append_child("Root");
    }

    _rootNode = _doc.child("Root");
}

void makeWeek(const std::string& from, const std::string& to)
{
    _weekNode = _rootNode.append_child("Week");
    _weekNode.append_attribute("From").set_value(from.c_str());
    _weekNode.append_attribute("To").set_value(to.c_str());
}

void fillWeek()
{
    auto activity = _weekNode.append_child("Activity");
    activity.append_attribute("Type").set_value("0");
}

int main()
{
    loadXML();

    makeWeek("15/10","19/10");
    makeWeek("08/10","12/10");

    sort();

    fillWeek();

    _doc.save_file("Test.xml");

    return 0;
}

How to solve this problem please ?

Edit (Output):

Here is the actual result :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
    <Week From="08/10" To="12/10" />
    <Week From="15/10" To="19/10" />
</Root>

And here is the desired result :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Root>
   <Week From="08/10" To="12/10">
       <Activity Type="0" />
   </Week>
   <Week From="15/10" To="19/10" />
</Root>
1
  • It'd make it clearer if you showed a small xml document and the expected result too. Commented Oct 30, 2023 at 18:59

1 Answer 1

2

Your sorting function removes and reinserts nodes so _weekNode seems to not be a reference to the latest inserted weeknode after sorting.

I suggest calling fillWeek() before sorting:

fillWeek();
sort();

If you want to do the sorting before calling fillWeek() you need to find the node you want to fill first.

void fillWeek()
{
    // _weekNode will now reference the first of the sorted nodes:
    _weekNode = _rootNode.first_child();
    auto activity = _weekNode.append_child("Activity");
    activity.append_attribute("Type").set_value("0");
}

You could also use

template<typename Predicate>
xml_node pugi::xml_node::find_node(Predicate pred) const

to find an arbitrary node.


If you instead move the nodes around in your sort() function, your _weekNode will keep the reference to the last week node you appended and you will not have to find it again:

#include <iterator>

void sort() {
    std::vector<pugi::xml_node> nodesToSort(
        std::move_iterator(_rootNode.begin()),
        std::move_iterator(_rootNode.end()));

    std::sort(nodesToSort.begin(), nodesToSort.end(), compareNodesByAttribute);

    for(pugi::xml_node node : nodesToSort) {
        _rootNode.append_move(node);
    }
}
Sign up to request clarification or add additional context in comments.

7 Comments

I would like to sort before filing the nodes, the sort() function restrict all the actions on the other nodes, is there any other solution to keep the reference for the latest inserted week node ?
@BoHalim You need to find the new node first. I added an example for doing that.
@BoHalim That was just one example to fit the question. To lookup any other node, you can use find_node. I added that too. You could also iterate using begin() and end() to do your own finding function if needed.
@BoHalim I think I found the best way forward. See the sort() function I suggest in the updated answer.
@BoHalim :-) Great that it worked out! Cheers!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.