jonbdk
jonbdk

Reputation: 48

C#: Sort xml nodes and childnodes using attribute value

Referencing the two questions here and here
I would like to sort on child nodes as well, but resetting for each "outer" node.
So:

<Root>
    <I aa="1">
        <s aa="3"/>
        <s aa="1"/>
        <s aa="2"/>
    </I>
    <I aa="5">
        <s aa="3"/>
        <s aa="1"/>
        <s aa="2"/>
    </I>
    <I aa="3">
        <s aa="3"/>
        <s aa="1"/>
        <s aa="2"/>
    </I>
    <I aa="4">
        <s aa="3"/>
        <s aa="1"/>
        <s aa="2"/>
    </I>
</Root>

Would become:

<Root>
    <I aa="1">
        <s aa="1"/>
        <s aa="2"/>
        <s aa="3"/>
    </I>
    <I aa="3">
        <s aa="1"/>
        <s aa="2"/>
        <s aa="3"/>
    </I>
    <I aa="4">
        <s aa="1"/>
        <s aa="2"/>
        <s aa="3"/>
    </I>
    <I aa="5">
        <s aa="1"/>
        <s aa="2"/>
        <s aa="3"/>
    </I>
</Root>

I tried different variations of the XDocument sorting, but I cannot get the syntax right.
Hoping someone can help, or provide me with an idea.

Upvotes: 1

Views: 741

Answers (2)

Eize Oosting
Eize Oosting

Reputation: 11

In my case, my XML was also containing string contents at the lowest level of the nodes. For this, I had to slightly amend the sorting code, otherwise that bit would be eliminated:

private static XElement CopyAndSort(XElement e)
{
    if (e.HasElements)
    {
        var elements = e.Elements()
            .OrderBy(keySelector: x => (string?)x.Attribute("name"))
            .Select(CopyAndSort);
        return new XElement(e.Name, e.Attributes(), elements);
    }
    return e;
}

Upvotes: 0

Charles Mager
Charles Mager

Reputation: 26223

You need some recursion here - each element needs to be cloned and its child elements sorted by the aa attribute. A method to do that might look like this:

private static XElement CopyAndSort(XElement e)
{
    var elements = e.Elements()
        .OrderBy(x => (string)x.Attribute("aa"))
        .Select(CopyAndSort);
    return new XElement(e.Name, e.Attributes(), elements);
}

If you then call that with your Root element, you will get back a new Root element with all its children sorted (and their children sorted, and so on).

See this fiddle for a working demo.

Upvotes: 1

Related Questions