QT DOMXml - Change the name of a node [duplicate] - c++

This question already has answers here:
Edit Value of a QDomElement?
(6 answers)
Closed 6 years ago.
I am working on a QT Project and part of that is a reconstruction of an XML file. I was able to make most of the needed changes with QDom but I can't find how to rename a node.
So the old XML file looks like ..
<root>
<window name="" ..>
<element x="" y=""/>
<element1 a="" b=""/>
...
</window>
..
..
<window name="">
<element x="" y=""/>
<element1 a="" b=""/>
...
</window>
</root>
How can i change the XML so that the new one will have < group > instead of < window >?
So at the end it needs to look like..
<root>
<group name="" ..>
<element x="" y=""/>
<element1 a="" b=""/>
...
</group>
..
..
<group name="">
<element x="" y=""/>
<element1 a="" b=""/>
...
</group>
</root>
Adding some more info...
Here is the code I use to read the <window> nodes, delete some based on the visibility (comes from a list) and I need to change <window> to <group> for the remaining nodes.
QFile oldXml("file.xml");
oldXml.open(QIODevice::ReadOnly);
QDomDocument doc;
doc.setContent(&oldXml);
QDomNodeList nodes = doc.elementsByTagName("window");
// Remove Window nodes based on visibility
insize = nodes.length();
for ( int i = 0; i < insize; i++ ) {
QDomNode node = nodes.at(i-dels);
if ( (list2[i] == "0") | (list2[i]=="") ) {
node.parentNode().removeChild(node);
dels=dels+1;
} else {
// Here is where i need to change the node name from <window> to e.g. <group>
}
}

You could use setTagName and maybe setAttribute if you want to set a value for the name attribute.
With the following example, myxml.xml is converted to xmlout.xml
Note#1: this is just an example: we're replacing only the first node.
Note#2: in this example, we're using two different files. Depending on your design, you could use the same or not.
myxml.xml
<root>
<window name="">
<element x="" y=""/>
<element1 a="" b=""/>
</window>
<window name="">
<element x="" y=""/>
<element1 a="" b=""/>
</window>
</root>
xmlout.xml
<root>
<group name="value">
<element y="" x=""/>
<element1 a="" b=""/>
</group>
<window name="">
<element y="" x=""/>
<element1 a="" b=""/>
</window>
</root>
main.cpp
#include <iostream>
#include <QtXml>
#include <QFile>
int main(int argc, char *argv[])
{
QDomDocument doc;
// Load xml file as raw data
QFile inFile(":myxml.xml");
if (!inFile.open(QIODevice::ReadOnly ))
{
std::cerr << "Error - inFile: " << inFile.errorString().toStdString();
return 1;
}
// Set data into the QDomDocument before processing
doc.setContent(&inFile);
// Get element in question
QDomElement root = doc.documentElement();
QDomElement nodeTag = root.firstChildElement("window");
nodeTag.setTagName("group");
nodeTag.setAttribute("name","value");
inFile.close();
// Save the modified data
QFile outFile("xmlout.xml");
if (!outFile.open(QIODevice::WriteOnly ))
{
// Error while loading file
std::cerr << "Error - outFile: " << outFile.errorString().toStdString();
return 1;
}
QTextStream stream;
stream.setDevice(&outFile);
stream.setCodec("UTF-8");
doc.save(stream,4);
outFile.close();
return 0;
}

I did not see any straight API function to rename the element. API is allowing to change value but not name.
There is another round about way.
Create an element, for example:
QDomElement group = doc.createElement("group");
use "QDomNode's replacechild" function.
QDomNode QDomNode::replaceChild(const QDomNode &newChild, const QDomNode &oldChild)
ex:
QDomNode dNode = node.replaceChild(group,oldNode);

Related

How do I use QXmlStreamReader to parse an XML file that contains references to other XML files?

I'm attempting to parse an xml file in C++ using a QXmlStreamReader (Qt 5.5.1). I'm using the XML file to map numeric keys to corresponding image files. I've gotten it to work with a simple XML file such as:
<?xml version="1.0" encoding="UTF-8"?>
<images>
<group1>
<image key="1" value="image1_group1.png"/>
<image key="2" value="image2_group1.png"/>
</group1>
<group2>
<image key="1" value="image1_group2.png"/>
<image key="2" value="image2_group2.png"/>
</group2>
</images>
using the following code:
#include <QFile>
xml_image_mapper::xml_image_mapper(QObject* p_parent_ptr)
{
QFile file("myfile.xml");
file.open(QFile::ReadOnly | QFile::Text);
QXmlStreamReader stream_reader(&file);
stream_reader.readNextStartElement();
while (stream_reader.readNextStartElement())
{
auto* inner_map_ptr = new QMap<quint32, QString>();
m_image_map.insert(stream_reader.name().toString(), inner_map_ptr);
parse_xml(stream_reader, *inner_map_ptr);
}
}
void xml_image_mapper::parse_xml(QXmlStreamReader& stream_reader, QMap<quint32, QString>& p_map)
{
while (stream_reader.readNextStartElement())
{
static const QString key_name = "key";
static const QString value_name = "value";
quint32 key;
QString value;
foreach (const QXmlStreamAttribute attribute, stream_reader.attributes())
{
if (key_name == attribute.name())
key = attribute.value().toInt();
else if (value_name == attribute.name())
value = attribute.value().toString();
p_map.insert(key, value);
}
stream_reader.skipCurrentElement();
}
}
This code correctly creates the map from numeric keys to filenames for the simple XML shown above, but fails to work for an XML file that includes references, such as:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE doc [
<!ENTITY group1 SYSTEM "group1.xml">
<!ENTITY group2 SYSTEM "group2.xml">
]>
<images>
<group1>
&group1;
</group1>
<group2>
&group2;
</group2>
</images>
Where group1.xml reads:
<?xml version="1.0" encoding="UTF-8"?>
<image key="1" value="image1_group1.png"/>
<image key="2" value="image2_group1.png"/>
and group2.xml reads:
<?xml version="1.0" encoding="UTF-8"?>
<image key="1" value="image1_group2.png"/>
<image key="2" value="image2_group2.png"/>
Is there a way to parse XML files with references to other XML files using a QXmlStreamReader?
From the Qt documentation:
QXmlStreamReader is a well-formed XML 1.0 parser that does not include external parsed entities.
So you seem to be out of luck here.

Adding a sub child to boost ptree

Lets say I am aiming to create an xml in the following form:
<main>
<elements>
<element name="elem1"><temp/>
</element>
<element name="elem2"><temp/>
</element>
</elements>
</main>
I have the following code:
ptree pt;
pt.put("main","");
ptree temp1;
temp1.put("element","");
temp1.put("element.<xmlattr>.name","elem1");
temp1.put("element.temp");
ptree temp2;
temp2.put("element","");
temp2.put("element.<xmlattr>.name","elem2");
temp2.put("element.temp");
//temp1 represents the 1st <element>...</element>
//temp2 represents the 1st <element>...</element>
//Add temp1 and temp2 to <main> under <elements>
I would assume the following would work:
pt.add_child("main.elements",temp1);
pt.add_child("main.elements",temp2);
However this generates the following xml:
<main>
<elements>
<element name="elem1"><temp/>
</element>
</elements>
<elements>
<element name="elem2"><temp/>
</element>
<elements>
</main>
I was able to get the required xml file by making my temp1 in the following form:
temp1.put("<xmlattr>.name","elem1");
temp1.put("temp","");
temp2.put("<xmlattr>.name","elem2");
temp2.put("temp","");
pt.add_child("main.elements.element",temp1);
pt.add_child("main.elements.element",temp2);
Is there a way I can work with my initial temp1 and temp2 nodes to get the desired xml structure?
Your situation is a bit suboptimal (and I'd very much favour the working snippet you gave).
Here's what would work:
pt.add_child("main.elements.element", temp1.get_child("element"));
pt.add_child("main.elements.element", temp2.get_child("element"));
Live On Coliru
#include <boost/property_tree/xml_parser.hpp>
#include <iostream>
using boost::property_tree::ptree;
int main() {
ptree temp1;
temp1.put("element.<xmlattr>.name","elem1");
temp1.put_child("element.temp", {});
ptree temp2;
temp2.put("element.<xmlattr>.name","elem2");
temp2.put("element.temp", "");
//Add temp1 and temp2 to <main> under <elements>
ptree pt;
pt.add_child("main.elements.element", temp1.get_child("element"));
pt.add_child("main.elements.element", temp2.get_child("element"));
write_xml(std::cout, pt, boost::property_tree::xml_writer_make_settings<std::string>(' ', 4, "utf-8"));
}
Prints
<?xml version="1.0" encoding="utf-8"?>
<main>
<elements>
<element name="elem1">
<temp/>
</element>
<element name="elem2">
<temp/>
</element>
</elements>
</main>

Adding subtree to boost::property_tree element

What I want is this:
<tree>
<objects>
<object id="12345678">
<AdditionalInfo>
<Owner>Mr. Heik</Owner>
<Health>37/100</Health>
</AdditionalInfo>
</object>
</objects>
</tree>
What I get is this:
<tree>
<objects>
<object id="12345678"/>
<AdditionalInfo>
<Owner>Mr. Heik</Owner>
<Health>37/100</Health>
</AdditionalInfo>
</objects>
</tree>
What I tried is this:
using boost::property_tree::ptree;
ptree pt;
boost::property_tree::ptree nodeObject;
nodeObject.put("object.<xmlattr>.id", 12345678);
boost::property_tree::ptree nodeInfo;
nodeInfo.put("Owner", "Mr. Heik");
nodeInfo.put("Health", "37/100");
// Put everything together
nodeObject.put_child("AdditionalInfo", nodeInfo);
pt.add_child("tree.objects", nodeObject);
write_xml("output.xml", pt);
I tried to get the desired result by using put/add/add_child/etc. but without success. Which boost-functions do I have to use?
This line :
nodeObject.put("object.<xmlattr>.id", 12345678);
Is adding a new child to the subpath "object" of the current node with the given attribute.
Just set your attribute on the Node:
nodeObject.put("<xmlattr>.id", 12345678);
And add the node directly to the correct path in your tree:
pt.add_child("tree.objects.object", nodeObject);
Final code :
ptree pt;
boost::property_tree::ptree nodeObject;
nodeObject.put("<xmlattr>.id", 12345678);
boost::property_tree::ptree nodeInfo;
nodeInfo.put("Owner", "Mr. Heik");
nodeInfo.put("Health", "37/100");
nodeObject.put_child("AdditionalInfo", nodeInfo);
pt.add_child("tree.objects.object", nodeObject);
write_xml("output.xml", pt);
Output :
<?xml version="1.0" encoding="utf-8"?>
<tree>
<objects>
<object id="12345678">
<AdditionalInfo>
<Owner>Mr. Heik</Owner>
<Health>37/100</Health>
</AdditionalInfo>
</object>
</objects>
</tree>

How to delete nested xml element using qt

i have xml file like this
<root>
<element>
<child id = "0"> Some Text </child> <-- Target To Delete
</element>
<element>
<child id = "1"> Some Text </child>
</element>
</root>
how can i delete child element of id "0" ? using Qt library.
QDomDocument doc;
doc.setContent(oldXml);
QDomNodeList nodes = doc.elementsByTagName("element");
for (int i = 0; i < nodes.count(); ++i)
{
QDomNode node = nodes.at(i);
QDomElement child = node.firstChildElement("child");
if (!child.isNull() && child.attribute("id") == "0")
{
node.removeChild(child);
}
}
QString newXml = doc.toString();

How to edit the end of the XML document end in Qt (writeEndDocument)?

I write a small program address book, contacts stored in the xml file. Here is the part off the code
void new_engine::main_window::write_to_db(const QString& n, const QString& s)
{
QFile m_db_file(m_db_path);
QString t("User");
m_db_file.open(QIODevice::Append);
QXmlStreamWriter wxml(&m_db_file);
wxml.setAutoFormatting(true);
if(m_size == 1)
{
wxml.writeStartDocument();
wxml.writeStartElement("Persons");
}
wxml.writeStartElement(t);
QString id = QString::number(m_size);
wxml.writeAttribute("id", id);
wxml.writeTextElement("Name", n);
wxml.writeTextElement("Surname", s);
wxml.writeEndElement();
wxml.writeEndDocument();
m_db_file.close();
}
but the problem is that after the first contact of the tag file is closed. here is the result
<?xml version="1.0" encoding="UTF-8"?>
<Persons>
<User id="1">
<Name>das</Name>
<Surname>vcvx</Surname>
</User>
</Persons>
<User id="2">
<Name>eqwevxcv</Name>
<Surname>xcvxcx</Surname>
</User>
<User id="3">
<Name>das</Name>
<Surname>dasdasd</Surname>
</User>
but it must be so
<Persons>
<User id="1">
<Name>das</Name>
<Surname>vcvx</Surname>
</User>
<User id="2">
<Name>eqwevxcv</Name>
<Surname>xcvxcx</Surname>
</User>
<User id="3">
<Name>das</Name>
<Surname>dasdasd</Surname>
</User>
</Persons>
How can we do this, after each new record will have to change document end ?
This is an example:
void write_to_db(QXmlStreamWriter& writer, QString id, QString name, QString surname)
{
writer.writeStartElement("User");
writer.writeAttribute(QXmlStreamAttribute("id", id));
writer.writeTextElement("Name", name);
writer.writeTextElement("Surname", surname);
writer.writeEndElement();
}
// This can be a file, or whatever iodevice your heart desires.
QString out;
QXmlStreamWriter writer(&out);
writer.writeStartDocument();
writer.writeStartElement("Persons");
write_to_db(writer, "1", "das", "vcvx");
write_to_db(writer, "2", "das", "vcvx");
write_to_db(writer, "3", "das", "vcvx");
writer.writeEndElement(); // Close <persons> tag
writer.writeEndDocument();
The output (using QXmlStreamWriter::setAutoFormatting/Indent):
<?xml version="1.0"?>
<Persons>
<User id="1">
<Name>das</Name>
<Surname>vcvx</Surname>
</User>
<User id="2">
<Name>das</Name>
<Surname>vcvx</Surname>
</User>
<User id="3">
<Name>das</Name>
<Surname>vcvx</Surname>
</User>
</Persons>