Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Qt 5 produce random attribute order in XML

Tags:

random

xml

qt

qt4

qt5

When switching from Qt 4.8 to Qt 5.x you may notice that each time you save XML document it produce random attribute order inside the file. It makes no problem for programaticaly reading the XML document because attributes are allowed to be stored in any order when XML is deserialized. It is a problem when you track changes of output XML files using GIT, SVN, etc. - it is not possible to tell if the data inside XML file changed or the attribute structure changed.

Is it possible to generate XML files in Qt 5.x the same way as in Qt 4.8?

like image 433
killdaclick Avatar asked Dec 09 '14 11:12

killdaclick


1 Answers

I tried play with the hash seed, but it works fine only if you use one machine. If a file created on first machine open on second the same code doesn't produce the same order even if i set the hash seed to the same value. So, i just decided to find another solution.

I decided to use QXmlStreamWriter class as a wrapper. Each time i save QDomDocument i parse it and write through QXmlStreamWriter. This helps me convert an xml DOM to canonical form.

Here is code i used. Maybe someone will find it useful.

bool MyDomDocument::SaveCanonicalXML(QIODevice *file, int indent, QString &error) const
{
  QXmlStreamWriter stream(file);
  stream.setAutoFormatting(true);
  stream.setAutoFormattingIndent(indent);
  stream.writeStartDocument();

  QDomNode root = documentElement();
  while (not root.isNull())
  {
    SaveNodeCanonically(stream, root);
    if (stream.hasError())
    {
        break;
    }
    root = root.nextSibling();
  }

  stream.writeEndDocument();

  if (stream.hasError())
  {
    error = tr("Fail to write Canonical XML.");
    return false;
  }
  return true;
}

void SaveNodeCanonically(QXmlStreamWriter &stream, const QDomNode &domNode)
{
  if (stream.hasError())
  {
    return;
  }

  if (domNode.isElement())
  {
    const QDomElement domElement = domNode.toElement();
    if (not domElement.isNull())
    {
        stream.writeStartElement(domElement.tagName());

        if (domElement.hasAttributes())
        {
            QMap<QString, QString> attributes;
            const QDomNamedNodeMap attributeMap = domElement.attributes();
            for (int i = 0; i < attributeMap.count(); ++i)
            {
                const QDomNode attribute = attributeMap.item(i);
                attributes.insert(attribute.nodeName(), attribute.nodeValue());
            }

            QMap<QString, QString>::const_iterator i = attributes.constBegin();
            while (i != attributes.constEnd())
            {
                stream.writeAttribute(i.key(), i.value());
                ++i;
            }
        }

        if (domElement.hasChildNodes())
        {
            QDomNode elementChild = domElement.firstChild();
            while (not elementChild.isNull())
            {
                SaveNodeCanonically(stream, elementChild);
                elementChild = elementChild.nextSibling();
            }
        }

        stream.writeEndElement();
    }
  }
  else if (domNode.isComment())
  {
    stream.writeComment(domNode.nodeValue());
  }
  else if (domNode.isText())
  {
    stream.writeCharacters(domNode.nodeValue());
  }
}
like image 79
dismine Avatar answered Oct 05 '22 15:10

dismine