2

I'm reading a document such as the following using XDocument:

<form>
    <hiddencontrols>
        <data classid="{5546E6CD-394C-4bee-94A8-4425E17EF6C6}" datafieldname="opportunityid" id="opportunityid"/>
        <data classid="{5546E6CD-394C-4bee-94A8-4425E17EF6C6}" datafieldname="producttypecode" id="producttypecode"/>
    </hiddencontrols>
</form>

Example load:

XmlReader rdr = XmlReader.Create(new System.IO.StringReader(xmlString));
XDocument xdoc = XDocument.Load(rdr, LoadOptions.None);
foreach (XElement xe in xdoc.Root.DescendantsAndSelf()) {
    if (xe.NodeType == XmlNodeType.Element) {
        if (xe.HasAttributes) {
             foreach (XAttribute xa in xe.Attributes()) {

Within the foreach to iterate the attributes, the original order of the attributes in the original XML is NOT preserved. That is to say, when I write out the attributes again from within the foreach, they are written out in a different order from the original XML.

Example output (of just the first data node):

<data id="new_opportunityid" datafieldname="opportunityid" classid="{5546E6CD-394C-4bee-94A8-4425E17EF6C6}"/>

I have tried to determine if there is some consistency applied (eg. whether the XElement parser always runs in reverse order or similar), there is no discernable pattern to the attribute order. I have also tried using just an XmlReader without loading into an XDocument, I have tried loading the XDocument from a MemoryStream instead of from the XmlReader. All approaches yield the same result.

I am aware that within the XML specification attribute order is not significant, this is important to me because I am doing attribute value transformation within the foreach, and I want to be able to diff the final document against the original without having to account for different ordering of the attributes, but just seeing changes in values. It also matters for diff size within source control.

Will Vousden
  • 32,488
  • 9
  • 84
  • 95
Ben
  • 199
  • 2
  • 13
  • 1
    Can't reproduce - see [this fiddle](https://dotnetfiddle.net/Vc8x3Q), they're in document order. What do you mean by 'write out the attributes again'? – Charles Mager Jan 08 '16 at 16:46
  • 1
    As an aside, the `NodeType` check isn't necessary, `DescendantsAndSelf` only returns elements. You could also simplify your parsing by just using `XDocument.Parse(xmlString)`. – Charles Mager Jan 08 '16 at 16:48
  • Have you verified in the debugger that after the reader / parser gets done with your xml that your elements have the attributes in the order you want? – Chuck Savage Jan 08 '16 at 17:32
  • @CharlesMager, by 'write out the attributes again' I mean that I am using an XmlWriter to reproduce the input Xml but modifying attribute and element values where necessary. Agreed about `DescendantsAndSelf`, I do actually need text nodes in order to change their values so I've now changed this line to `foreach (XNode xe1 in ((XElement)xn).Nodes())` – Ben Jan 11 '16 at 09:24
  • @ChuckSavage yes I did check this in the debugger, and no they are not in the input xml order (which is the order I 'want'). – Ben Jan 11 '16 at 09:25
  • The fiddle shows they *are* in order. You need to give a reproducible example, as it would seem your issue isn't in the code you've included. – Charles Mager Jan 11 '16 at 09:51
  • In your case, attributes order are reversed. – Savaratkar Jan 11 '16 at 10:31

2 Answers2

3

Well, this is bizarre. I randomly found a solution this morning. This is my current setup sequence for parsing the document:

XDocument xdoc = XDocument.Parse(form.FormXml);
StringBuilder destXml = new StringBuilder();
XmlWriterSettings xrs = new XmlWriterSettings
{
    Indent = false,
    NewLineChars = String.Empty,
    WriteEndDocumentOnClose = true
};

XmlWriter xr = XmlWriter.Create(destXml, xrs);

The change I made this morning was to change Indent = false from true and NewLineChars = String.Empty from Environment.NewLine in the XmlWriterSettings. Hey presto, attributes are coming out in the same order they go in!! I have no idea why this is the case, but I've tested it a couple of times and it is consistent...

Ben
  • 199
  • 2
  • 13
-1
xe.Attributes

returns collection of attributes and collections in C# not always retains the order.

Referenced from: Do C# collections always enforce order?

It says Enumerable.Select retains the order. Can you try that for yourself and check.

Community
  • 1
  • 1
Savaratkar
  • 1,974
  • 1
  • 24
  • 44