Сортировка всех элементов в XDocument
У меня есть 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?
Спасибо за любые идеи.
Ответы
Ответ 1
У вас уже есть способ сортировки элементов. Просто примените его рекурсивно:
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));
}
Ответ 2
ЭТОТ СПОСОБ ПРИНЯТЬ РАСШИРЕНИЕ РЕАЛЬНОГО ДОКУМЕНТА И СОХРАНЕНИЕ АТРИБУТОВ И ТЕКСТОВЫХ ЗНАЧЕНИЙ
Я придумал это, основываясь на нескольких разных сообщениях и кодексах здесь и там... Спасибо всем, кто внес свой вклад!
В том же пространстве имен (не в том же классе) добавьте следующее...
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();
Ответ 3
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 с использованием С# и ASP.NET, и я был очень рад, когда нашел этот поток. Благодаря всем, это сделало именно то, что мне было нужно.
~ Matt
Ответ 4
Я думаю, что этот метод расширения работает лучше всего.
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));
}
}