0

If I have a DOMDocument and I would like to parse it into a string, how do I ensure that the attributes will be sorted in some deterministic/normalized order (lexicographically based on the attribute name would be nice)?

For example how can I make this print equal instead of notequal?

 $dom = new DOMDocument();
 $dom->loadXML('<tag a="a" b="b"/>');
 
 $dom2 = new DOMDocument();
 $dom2->loadXML('<tag b="b" a="a"/>');

 echo $dom->saveXML() === $dom2->saveXML() ? "equal" : "notequal";

According to the documentation, saveXML only supported option is LIBXML_NOEMPTYTAG.

Ricola
  • 2,621
  • 12
  • 22
  • 1
    Apart from your example, may I ask why you would want to do this? Normally the order of the attributes is irrelevant. – KIKO Software Apr 14 '21 at 14:21
  • 1
    @KIKO Software the reason behind it in my case is quite silly (trying to fix legacy code with minimum changes) but in general I don't think the question is that silly, as you can see in [this answer](https://stackoverflow.com/a/11263710/7424948) for a similar question in Java – Ricola Apr 14 '21 at 16:06
  • 2
    I didn't ask for the "why" because I think the question is silly, I asked it to see if there is an alternative approach. People often ask only exactly "what" they want to do, without explaining the "why". If, for instance, you had asked us how you could fix legacy code with minimum changes, you might have gotten surprised by the answers. Of course you would need to supply the code, what you want changed, and your attempt at doing it. If your attempt is the correct approach people will help you to improve it, but there might be a better approach you didn't even think of. – KIKO Software Apr 14 '21 at 17:05
  • In the case of the current question, you looked for the answer in the options of `saveXMS()`, whereas I think it would make more sense to sort the dom nodes before you save them. – KIKO Software Apr 14 '21 at 17:07

1 Answers1

2

DOMNode::C14N() canonicalizes nodes to a string.

$expected = new DOMDocument();
$expected->loadXML('<tag a="a" b="b"/>');

$actual = new DOMDocument();
$actual->loadXML('<tag b="b" a="a"/>');

echo $expected->C14N() === $actual->C14N() ? "equal" : "notequal";

Output:

equal

If you're write unit tests - PHPUnit has specific assertions for XML.

  • assertXmlFileEqualsXmlFile()
  • assertXmlStringEqualsXmlFile()
  • assertXmlStringEqualsXmlString()
ThW
  • 19,120
  • 3
  • 22
  • 44
  • `->C14N()` returns `false` to me even though I pass it a valid xml. How can I spot the error (get a stacktrace or something) instead of just getting a `false` value? – Ricola Jun 15 '21 at 13:26
  • Actually I think my issue is that we have relative URIs : https://github.com/robrichards/xmlseclibs/issues/210#issuecomment-762415843 Too bad I can't really use `C14N` unless I fix my URIs first – Ricola Jun 15 '21 at 16:28