Update node value using RapidXML

53 Views Asked by At

I'm trying to edit the value of a node using RapidXML 1.13 but the changes aren't made to the .xml file.

A simplified version of the XML file looks like this:

<users>
    <user>
        <username>test</username>
        <!-- This hazard line should change to "true" -->
        <hazards>false</hazards>
        <weather>false</weather>
    </user>
</users>

I use a simple for to iterate through all users. When the matching one is found, I select the attribute I am interested in and try to modify its content.

rapidxml::file<> xmlFile("users.xml");
rapidxml::xml_document<> doc;
doc.parse<0>(xmlFile.data());

for (rapidxml::xml_node<>* userNode = doc.first_node("users")->first_node("user"); userNode; userNode = userNode->next_sibling("user")) 
{
    rapidxml::xml_node<>* usernameNode = userNode->first_node("username");
    if (usernameNode && strcmp(usernameNode->value(), username) == 0) 
    {
        rapidxml::xml_node<>* settingNode = userNode->first_node(setting.c_str());
        if (settingNode)
        {
            // Here's the problem
            settingNode->value(doc.allocate_string(boolToChar(value)));
            std::ofstream outFile("users.xml");
            outFile << doc;
            outFile.close();
    
            std::cout << settingNode->value() << std::endl;
            rapidxml::print(std::cout, doc, 0);
            return; // Don't keep the for running
        }
    }                
    // Other edge cases...
}

This line std::cout << settingNode->value() << std::endl; prints "true" (the correct new value), but this rapidxml::print(std::cout, doc, 0); prints the old, unchanged XML file. Therefore, the value seems to be modified, but never saved into the doc file.

I've tried to create a clone of the node, delete the original and insert the modified clone, but the same problem occurs: can't change the value. boolToChar() function is correct, the exact same method works when trying to use allocate_string() to a new generated node.

EDIT

Found the problem: This line settingNode->value(doc.allocate_string(boolToChar(value))); transforms to settingNode->first_node()->value(doc.allocate_string(boolToChar(value)));. From what I can tell, the text inside the nodes are considered nodes too, thus explaining the first_node() extra call.

1

There are 1 best solutions below

0
Yitzhak Khabinsky On

Here is XSLT based solution. One single template does the job.

There are few libraries available for XSLT in c++.

Input XML

<users>
    <user>
        <username>test</username>
        <hazards>false</hazards>
        <weather>false</weather>
    </user>
</users>

XSLT 1.0

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" omit-xml-declaration="yes"
                encoding="UTF-8" indent="yes"/>
    <xsl:strip-space elements="*"/>

    <!--Identity transform-->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="hazards">
        <xsl:copy>
            <xsl:text>true</xsl:text>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output XML

<users>
  <user>
    <username>test</username>
    <hazards>true</hazards>
    <weather>false</weather>
  </user>
</users>