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>
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);
}
}