2010-08-12 18 views
12

Tengo un XDocument donde me gustaría ordenar todos los elementos alfabéticamente. Aquí hay una versión simplificada de la estructura:Ordenando todos los elementos en un 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> 

Estoy queriendo ordenar los elementos en estos documentos a todos los niveles, hasta ahora soy capaz de solucionar el problema de esta manera:

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)); 
} 

Qué produce:

<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> 

me gustaría ser capaz de ordenar todos los elementos secundarios de la misma manera (a través de una función recursiva idealmente). ¿Alguna idea de cómo puedo hacer esto con LINQ?

Gracias por cualquier idea.

Respuesta

18

Ya tiene un método para ordenar los elementos. Sólo tienes que aplicar de forma recursiva:

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)); 
} 

Tenga en cuenta que esta quita todos los nodos que no son elementos (atributos, texto, comentarios, etc.) de su documento.


Si desea mantener los nodos que no son elementos, hay que copiarlos:

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)); 
} 
+0

Gracias , Pude usar esto para obtener mi resultado deseado con los atributos y el texto. Aclamaciones. –

+0

Esta es una gran manera de mostrar cómo ordenar los nodos, pero deshacerse de todos los valores y atributos no es una respuesta que responda a la pregunta de [email protected] hubiera sido genial si hubieras editado esta respuesta mostrando a todos los demás cómo obtuviste la respuesta correcta que estabas buscando. –

+0

@ArvoBowen: He actualizado mi respuesta. – dtb

4
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)); 
} 

Aquí es un ejemplo actualizado que incluirá todos los atributos cuando se realiza la clasificación.

Esta publicación me ayudó mucho, porque no quería realizar una ordenación XML utilizando XSLT ya que no quería reformatear el XML. Busqué una solución simple para realizar una ordenación XML usando C# y ASP.NET y estuve encantada cuando encontré este hilo. Gracias a todos, esto hizo exactamente lo que necesitaba.

~ Matt

+0

Esto no logrará lo que quiere el OP. ¡Esto eliminará todos los valores! –

-1

Creo que estos métodos de extensión funcionan mejor.

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)); 
    } 
} 
+0

Esta fue una gran base para seguir pero no conserva todos los valores de texto o los atributos. Así que tuve que agregar mi propia respuesta que hizo con algunas modificaciones. ;) –

10

este método hace una extensión verdadero documento y conserva los atributos y valores de texto

me ocurrió con esta base de un par de mensajes diferentes y el código de aquí y allá ... Gracias a todos ¡Quién contribuyó!

Dentro del mismo espacio de nombres (no la misma clase) añadir el siguiente ...

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)); 
    } 
} 

utilizar la extensión del documento ...

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

//Sort the root element 
xDoc.Root.Sort(); 
+0

Esta es la mejor respuesta aquí. Solo funcionó. ¡Gracias! –

+0

cambiando una línea a: Lista ordenadosChildren = fuente.Elementos(). OrderBy (elem => elem.Attributes ("Nombre"). Any()? Elem.Attributes ("Nombre"). Primero(). Valor .ToString(): string.Empty) .ToList(); Y funciona genial para ordenar los archivos de edmx. Gracias Arvo –

Cuestiones relacionadas