Сортировка всех элементов в XDocument - PullRequest
13 голосов
/ 12 августа 2010

У меня есть XDocument, где я бы хотел отсортировать все элементы в алфавитном порядке. Вот упрощенная версия структуры:

<Config>
 <Server>
    <Id>svr1</Id>
    <Routing>
        <RoutingNodeName>route1</RoutingNodeName>
        <Subscription>
            <Id>1</Id>
        </Subscription>
        <RoutingParameters id="Routing1">
            <Timeout>7200</Timeout>
        </RoutingParameters>
    </Routing>
    <Storage>
            <Physical>HD1</Physical>
    </Storage>
 </Server>
 <Applications>
    <Services>
        <Local></Local>
    </Services>
 </Applications>
</Config>

Я хочу отсортировать элементы в этих документах на всех уровнях, поэтому пока я могу отсортировать их так:

private static XDocument Sort(XDocument file)
{
    return new XDocument(
        new XElement(file.Root.Name,
            from el in file.Root.Elements()
            orderby el.Name.ToString()
            select el));
}

Который производит:

<Config>
<Applications>
  <Services>
    <Local></Local>
  </Services>
</Applications>
<Server>
  <Id>svr1</Id>
  <Routing>
    <RoutingNodeName>route1</RoutingNodeName>
    <Subscription>
      <Id>1</Id>
    </Subscription>
    <RoutingParameters id="Routing1">
      <Timeout>7200</Timeout>
    </RoutingParameters>
  </Routing>
  <Storage>
    <Physical>HD1</Physical>
  </Storage>
</Server>
</Config>

Я бы хотел сортировать все дочерние элементы одинаково (в идеале, через рекурсивную функцию). Любые идеи, как я могу добиться этого с LINQ?

Спасибо за любые идеи.

Ответы [ 4 ]

19 голосов
/ 12 августа 2010

У вас уже есть метод сортировки элементов. Просто примените его рекурсивно:

private static XElement Sort(XElement element)
{
    return new XElement(element.Name,
            from child in element.Elements()
            orderby child.Name.ToString()
            select Sort(child));
}

private static XDocument Sort(XDocument file)
{
    return new XDocument(Sort(file.Root));
}

Обратите внимание, что это удаляет все неэлементные узлы (атрибуты, текст, комментарии и т. Д.) Из вашего документа.


Если вы хотите сохранить неэлементные узлы, вы должны скопировать их:

private static XElement Sort(XElement element)
{
    return new XElement(element.Name,
            element.Attributes(),
            from child in element.Nodes()
            where child.NodeType != XmlNodeType.Element
            select child,
            from child in element.Elements()
            orderby child.Name.ToString()
            select Sort(child));
}

private static XDocument Sort(XDocument file)
{
    return new XDocument(
            file.Declaration,
            from child in file.Nodes()
            where child.NodeType != XmlNodeType.Element
            select child,
            Sort(file.Root));
}
13 голосов
/ 11 октября 2013

ЭТОТ МЕТОД ДЕЛАЕТ РЕАЛЬНОЕ РАСШИРЕНИЕ ДОКУМЕНТА И СОХРАНЯЕТ АТРИБУТЫ И ЗНАЧЕНИЯ ТЕКСТА

Я придумал это, основываясь на нескольких разных постах и ​​коде здесь и там ... Спасибо всем, кто внес свой вклад!

В том же пространстве имен (не в том же классе) добавить следующее ...

public static void Sort(this XElement source, bool bSortAttributes = true)
{
    //Make sure there is a valid source
    if (source == null) throw new ArgumentNullException("source");

    //Sort attributes if needed
    if (bSortAttributes)
    {
        List<XAttribute> sortedAttributes = source.Attributes().OrderBy(a => a.ToString()).ToList();
        sortedAttributes.ForEach(a => a.Remove());
        sortedAttributes.ForEach(a => source.Add(a));
    }

    //Sort the children IF any exist
    List<XElement> sortedChildren = source.Elements().OrderBy(e => e.Name.ToString()).ToList();
    if (source.HasElements)
    {
        source.RemoveNodes();
        sortedChildren.ForEach(c => c.Sort(bSortAttributes));
        sortedChildren.ForEach(c => source.Add(c));
    }
}

Чтобы использовать расширение документа ...

//Load the xDoc
XDocument xDoc = XDocument.Load("c:\test.xml");

//Sort the root element
xDoc.Root.Sort();
4 голосов
/ 15 февраля 2011
private static XElement Sort(XElement element)
{
    XElement newElement = new XElement(element.Name,
        from child in element.Elements()
        orderby child.Name.ToString()
        select Sort(child));
    if (element.HasAttributes)
    {
        foreach (XAttribute attrib in element.Attributes())
        {
            newElement.SetAttributeValue(attrib.Name, attrib.Value);
        }
    }
    return newElement;
}

private static XDocument Sort(XDocument file)
{
    return new XDocument(Sort(file.Root));
}

Вот обновленный пример, который будет включать все атрибуты при выполнении сортировки.

Этот пост мне очень помог, потому что я не хотел выполнять сортировку XML с использованием XSLT, поскольку не хотел переформатировать XML. Я искал простое решение для выполнения сортировки XML с использованием C # и ASP.NET, и я был рад, когда нашел этот поток. Спасибо всем, это сделало именно то, что мне было нужно.

~ Matt

0 голосов
/ 25 января 2013

Я думаю, что этот метод расширения работает лучше всего.

public static class XmlLinq
{
  public static void Sort(this XElement source, bool sortAttributes = true)
  {
     if (source == null)
        throw new ArgumentNullException("source");

     if (sortAttributes)
        source.SortAttributes();

     List<XElement> sortedChildren = source.Elements().OrderBy(e => e.Name.ToString()).ToList();
     source.RemoveNodes();

     sortedChildren.ForEach(c => source.Add(c));
     sortedChildren.ForEach(c => c.Sort());
  }

  public static void SortAttributes(this XElement source)
  {
     if (source == null)
        throw new ArgumentNullException("source");

     List<XAttribute> sortedAttributes = source.Attributes().OrderBy(a => a.ToString()).ToList();
     sortedAttributes.ForEach(a => a.Remove());
     sortedAttributes.ForEach(a => source.Add(a));
  }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...