Modify an XML file with QDomDocument
-
Hi,
I am modifying an XML file using QDomDocument.
-
When I want to update the xml file, it adds the xmlns attribute to each tag.
-
I have tried to remove the xmlns tag by navigating through each tag, but it cannot remove it because it cannot find the xmlns tag in the items.
-
QDomElement works as if there is no xmlns tag in the generatorElement content, but when I want to write to the file, it adds the xmlns tag. How can I fix it?
Xml file:
<?xml version="1.0" encoding="UTF-8"?> <entity xmlns="http://www.sample.com.tr/EntityConfSchema" xmlns:attr="http://www.sample.com.tr/AttributeTypes" xmlns:sim="http://www.sample.com.tr/DataTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <sample-info> <sample-type>1111111</sample-type> </sample-info> <sample-initials> <attribute-nvp name="sampleName"> <attribute xsi:type="attr:StringAttribute" value="sampleValue"/> </attribute-nvp> <attribute-nvp name="sampleName"> <attribute xsi:type="attr:StringAttribute" value="sampleValue"/> </attribute-nvp> <attribute-nvp name="sampleName"> <attribute xsi:type="attr:StringAttribute" value="sampleValue"/> </attribute-nvp> </sample-initials> </entity>
Here's my code:
void SampleWidget::on_pushButtonSave_clicked() { QString entityCurrentFile = "sampleFile.entity"; QString fileToCopy = "fileToCopy.entity"; if (QFile::exists(entityCurrentFile)) { QFile::remove(entityCurrentFile); } QFile::copy(fileToCopy , entityCurrentFile); QFile file(entityCurrentFile); if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) { SIM_LOG_ERROR("CANNOT OPEN"); return; } QDomDocument* generatorDocument = new QDomDocument; if (!generatorDocument->setContent(&file, true)) { return; } file.close(); //checked the xmlData , it appears to be corrupted. There is xmlns attribute in every tag QByteArray xmlData = generatorDocument->toByteArray(); QDomElement generatorElement = generatorDocument->documentElement(); removeXmlnsAttributesFromDocument(generatorElement); modifyEntityFile(generatorElement); // modify QFile outputFile(entityCurrentFile); // Save content back to the file if (!outputFile.open(QIODevice::Truncate | QIODevice::WriteOnly)) { return; } QTextStream stream(&outputFile); stream << generatorDocument->toByteArray(); QByteArray xmlData = generatorDocument->toByteArray(); outputFile.close(); } void SampleWidget::removeXmlnsAttributesFromDocument(QDomNode& node) { QDomNodeList children = node.childNodes(); for (int i = 0; i < children.size(); ++i) { QDomNode childNode = children.at(i); if (childNode.isElement()) { QDomElement element = childNode.toElement(); QString attributeName = "xmlns"; if (element.hasAttribute(attributeName)) { element.removeAttribute(attributeName); qDebug() << "Attribute" << attributeName << "removed from node" << attributeName; } else { qDebug() << "Node" << attributeName << "does not have attribute" << attributeName; } } removeXmlnsAttributesFromDocument(childNode); } }
Result:
<?xml version="1.0" encoding="UTF-8"?> <entity xmlns="http://www.sample.com.tr/EntityConfSchema"> <sample-info xmlns="http://www.sample.com.tr/EntityConfSchema"> <sample-type xmlns="http://www.sample.com.tr/EntityConfSchema">111111</sample-type> </sample-info> <sample-initials xmlns="http://www.sample.com.tr/EntityConfSchema"> <attribute-nvp xmlns="http://www.sample.com.tr/EntityConfSchema" name="ExampleName"> <attribute xmlns="http://www.sample.com.tr/EntityConfSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="attr:StringAttribute" value="ExampleValue"/> </attribute-nvp> <attribute-nvp xmlns="http://www.sample.com.tr/EntityConfSchema" name="ExampleName"> <attribute xmlns="http://www.sample.com.tr/EntityConfSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="attr:StringAttribute" value="ExampleValue"/> </attribute-nvp> <attribute-nvp xmlns="http://www.sample.com.tr/EntityConfSchema" name="ExampleName"> <attribute xmlns="http://www.sample.com.tr/EntityConfSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="attr:StringAttribute" value="ExampleValue"/> </attribute-nvp> </sample-initials> </entity>
Expected xml file:
<?xml version="1.0" encoding="UTF-8"?> <entity xmlns="http://www.sample.com.tr/EntityConfSchema" xmlns:attr="http://www.sample.com.tr/AttributeTypes" xmlns:sim="http://www.sample.com.tr/DataTypes" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <sample-info> <sample-type>88888888</sample-type> </sample-info> <sample-initials> <attribute-nvp name="ExampleName"> <attribute xsi:type="attr:StringAttribute" value="ExampleValue"/> </attribute-nvp> <attribute-nvp name="ExampleName"> <attribute xsi:type="attr:StringAttribute" value="ExampleValue"/> </attribute-nvp> <attribute-nvp name="ExampleName"> <attribute xsi:type="attr:StringAttribute" value="ExampleValue"/> </attribute-nvp> </sample-initials> </entity>
-
-
@Ceng0
I have had a look at this.The text it saves may not be what you or I, as humans, would like, but it is still "correct".
It chooses to repeat the namespace attributes on each element. I believe this is deliberate/hard-coded, so that it is always right. I came across somewhere (e.g. https://codebrowser.dev/qt5/qtbase/src/xml/dom/qdom.cpp.html#_ZNK15QDomAttrPrivate4saveER11QTextStreamii):
* QDomElementPrivate::save() output a namespace declaration if * the element is in a namespace, no matter what
What it is not doing is taking into account default namespace, which we would like it to do. I have not found any handling for this or way of forcing it. I agree there is some special handling where we cannot see these as attribute nodes after reading.
If you passed
false
instead oftrue
tosetContent()
you would get the original document content back as-was. You would also see the original attributes retained. I do not know if you can work with that --- depends what you are doing with/to the document --- but that is the simplest and may be only way.You might alternatively do your own writing via
QXmlStreamWriter
to get control over this, but I think that's quite a bit work and seems silly if you have aQDomDocument
. -
@Ceng0
I assume that you are using Qt 5 because in Qt 6 the second argument to QDomDocument::setContent() is not a bool.Depending on your actual requirements you may be able to achieve the desired result without turning on namespace handling. This, for example, modifies the document but does not add namespace attribute everywhere:
#include <QCoreApplication> #include <QDomDocument> #include <QFile> int main(int argc, char** argv) { QCoreApplication app(argc, argv); QFile file("input.xml"); if (file.open(QIODevice::ReadOnly)) { QDomDocument doc; doc.setContent(&file, false); QDomElement docElem = doc.documentElement(); QDomElement first = docElem.firstChildElement("sample-initials"); first.setAttribute("xyzzy", "plugh"); QFile out("output.xml"); if (out.open(QIODevice::WriteOnly)) { out.write(doc.toByteArray(2)); } } return 0; }
Edit: Oops, just noticed that JonB already suggested this
-
@ChrisW67 said in Modify an XML file with QDomDocument:
doc.setContent(&file, false);
Yes, that is the
false
for "namespace processing" I was suggesting, which would be by far the simplest. Then everything stays like it is.But it depends on what the OP wants to do with the document read in (e.g. searching it?), if there are other namespaces involved, if they want to insert new nodes, etc.
-