2010-08-12 46 views
12

我有一個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?

感謝您的任何想法。

回答

18

您已經有了對元素進行排序的方法。只要遞歸地應用它:

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

謝謝,我能夠使用它來獲得我的預期結果與屬性和文本。乾杯。 – 2010-08-12 19:50:46

+0

這是展示如何對節點進行排序的好方法,但是擺脫所有值和屬性並不是解決OP問題的答案。@ZeroCool如果你已經編輯了這個答案,向其他人展示你是如何得到你正在尋找的正確答案的話,那就太棒了。 – 2013-10-11 18:04:16

+0

@ArvoBowen:我已經更新了我的答案。 – dtb 2013-10-11 18:24:13

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

這裏是一個更新的例子進行排序時,將包括所有的屬性。

這篇文章對我有很大幫助,因爲我不想重新格式化XML,所以我不想使用XSLT執行XML排序。我全面搜索了一個使用C#和ASP.NET執行XML排序的簡單解決方案,當我發現此線程時,我很高興。多虧了這一切,這正是我所需要的。

〜馬特

+0

這不會完成OP想要的。這會刪除所有的值! – 2013-10-11 18:00:43

-1

我覺得這些擴展方法工作最好的。

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

這對我來說是一個很好的基礎,但它並不保留所有的文本值或屬性。所以我不得不添加我自己的答案,做一些修改。 ;) – 2013-10-11 17:58:23

10

這種方法使一個真正的文件的擴展名,並保留屬性和文本值

我來到了這裏,從這個基礎關閉了幾個不同的崗位和代碼,並有...感謝所有誰貢獻!

在同一個命名空間(而不是同一類)中添加以下...

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

這是這裏最好的答案。剛剛工作。謝謝! – 2015-03-24 22:05:42

+0

將一行更改爲: 列表 sortedChildren = source.Elements()。OrderBy(elem => elem.Attributes(「Name」)。Any()?elem.Attributes(「Name」)。First()。Value .ToString():string.Empty).ToList(); 這對排序edmx文件很有用。感謝阿沃 – 2015-09-18 18:38:49