c# sorting linq-to-xml

c# - Ordenando todos los elementos en un XDocument



sorting linq-to-xml (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í hay un ejemplo actualizado que incluirá todos los atributos al realizar el ordenamiento.

Esta publicación me ayudó mucho, porque no quería realizar una ordenación XML utilizando XSLT porque no quería volver a formatear 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

Tengo un XDocument donde me gustaría ordenar todos los elementos alfabéticamente. Aquí hay una versión simplificada de la estructura:

<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 clasificar los elementos en estos documentos en todos los niveles, hasta ahora puedo ordenarlo 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)); }

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


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


Ya tienes un método para ordenar los elementos. Solo aplícalo recursivamente:

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 esto elimina todos los nodos de elemento (atributos, texto, comentarios, etc.) de su documento.

Si desea conservar los nodos de elementos, debe 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)); }


ESTE MÉTODO HACE UNA EXTENSIÓN DE DOCUMENTO REAL Y CONSERVA LOS ATRIBUTOS Y VALORES DE TEXTO

Se me ocurrió esto basado en algunas publicaciones y códigos diferentes de aquí y allá ... ¡Gracias a todos los que contribuyeron!

Dentro del mismo espacio de nombres (no la misma clase) agregue lo 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)); } }

Para usar la extensión del documento ...

//Load the xDoc XDocument xDoc = XDocument.Load("c:/test.xml"); //Sort the root element xDoc.Root.Sort();