2016-09-01 63 views
4

我正在爲這種排序而苦苦掙扎,需要一點幫助。使用LINQ排序

我將按排名排序子節點並保存它。 例如要訂購主要元素內的子節點,我將傳遞特定部分的ID。

這個例子只適用於第一後裔,現在我堅持下去。

XElement x = XElement.Load(xmlString1); 
x.Descendants("opt").First().ReplaceNodes(x.Descendants("opt").First() 
.Descendants("sel").OrderBy(o => int.Parse(o.Attribute("rank").Value))); 
4x.Save(xmlString2); 

我需要那樣。

x.Descendants("sub").Where(b => b.Attribute("id").Value == "DFG") 
.ReplaceNodes(x.Descendants("opt").First() 
.Descendants("sel").OrderBy(o => int.Parse(o.Attribute("rank").Value)) 

原始

<main id="AFB" rank="1" name="ROOT"> 
<sub id="DFG" rank="2" name="SUB1"> 
<att > 
    <sel id="JIK" rank="4" name="444" /> 
    <sel id="OKI" rank="2" name="222" /> 
    </att> 
    <opt> 
    <sel id="JIK" rank="2" name="122" /> 
    <sel id="OKI" rank="1" name="111" /> 
    </opt> 
</sub> 
<sub id="EGG" rank="1" name="SUB2" > 
    <opt> 
    <sel id="DJI" rank="1" name="111" /> 
    <sel id="LOW" rank="3" name="333" /> 
    <sel id="QWE" rank="2" name="222" /> 
    </opt> 
</sub> 
<main> 

目標

<main id="AFB" rank="1" name="ROOT"> 
<sub id="EGG" rank="1" name="SUB2" > 
    <opt> 
    <sel id="DJI" rank="1" name="111" />   
    <sel id="QWE" rank="2" name="222" /> 
    <sel id="LOW" rank="3" name="333" /> 
    </opt> 
</sub> 
<sub id="DFG" rank="2" name="SUB1"> 
    <att > 
    <sel id="OKI" rank="2" name="222" /> 
    <sel id="JIK" rank="4" name="444" />   
    </att> 
    <opt> 
    <sel id="OKI" rank="1" name="111" /> 
    <sel id="JIK" rank="2" name="122" />   
    </opt> 
</sub> 
<main> 
+1

我編輯您的文章,以正確地格式化代碼,使其可讀。但'4x.Save(xmlString2);'在那裏,編輯時我不更改* code *。這是一個錯字嗎? –

+1

你嘗試過遞歸嗎? – slawekwin

回答

1

我認爲你有一個錯別字出現。然而,看看這個解決方案:

var text = @" 
<main id='AFB' rank='1' name='ROOT'> 
    <sub id='DFG' rank='2' name='SUB1'> 
     <opt> 
      <sel id='JIK' rank='4' name='444' /> 
      <sel id='OKI' rank='2' name='222' /> 
     </opt> 
     <opt> 
      <sel id='JIK' rank='2' name='122' /> 
      <sel id='OKI' rank='1' name='111' /> 
     </opt> 
    </sub> 
    <sub id='EGG' rank='1' name='SUB2' > 
     <opt> 
      <sel id='DJI' rank='1' name='111' /> 
      <sel id='LOW' rank='3' name='333' /> 
      <sel id='QWE' rank='2' name='222' /> 
     </opt> 
    </sub> 
</main>"; 

var x = XDocument.Parse(text); 
x.Root.ReplaceNodes(x.Descendants("sub").OrderBy(a => int.Parse(a.Attribute("rank").Value))); 
foreach (var opt in x.Descendants("opt")) 
    opt.ReplaceNodes(opt.Descendants("sel").OrderBy(a => int.Parse(a.Attribute("rank").Value))); 

此時x包含以下XML:

<main id="AFB" rank="1" name="ROOT"> 
    <sub id="EGG" rank="1" name="SUB2"> 
    <opt> 
     <sel id="DJI" rank="1" name="111" /> 
     <sel id="QWE" rank="2" name="222" /> 
     <sel id="LOW" rank="3" name="333" /> 
    </opt> 
    </sub> 
    <sub id="DFG" rank="2" name="SUB1"> 
    <opt> 
     <sel id="OKI" rank="2" name="222" /> 
     <sel id="JIK" rank="4" name="444" /> 
    </opt> 
    <opt> 
     <sel id="OKI" rank="1" name="111" /> 
     <sel id="JIK" rank="2" name="122" /> 
    </opt> 
    </sub> 
</main> 

如果att是存在,而不是「opt`,應包括,下面的工作:

var x = XDocument.Parse(text); 
x.Root.ReplaceNodes(x.Descendants("sub").OrderBy(a => int.Parse(a.Attribute("rank").Value))); 
foreach (var opt in x.Descendants("sub").Elements()) 
    opt.ReplaceNodes(opt.Descendants("sel").OrderBy(a => int.Parse(a.Attribute("rank").Value))); 

如果您需要以下的名字單個元素,使用排序(如果沒有等級屬性存在,或者是空的,穿上後):

//sub with id=EGG 
var sub2 = x.Descendants("sub").FirstOrDefault(a => a.Attribute("id").Value == "EGG"); 
if (sub2 != null) 
{ 
    foreach (var node in sub2.Elements()) 
     node.ReplaceNodes(node.Elements().OrderBy(a => 
     { 
      int rank; 
      if (a.Attribute("rank") == null || !int.TryParse(a.Attribute("rank").Value, out rank)) 
       rank = int.MaxValue; 
      return rank; 
     })); 
} 
+0

非常感謝您的快速回答。我會試試看。 – andy

+0

增加了另一個示例。 –

+0

?是空的條件運算符(https://msdn.microsoft.com/en-us/library/dn986595.aspx)以防止出現'NullReferenceException'。什麼是錯誤? –

0

即使這不是對您的問題的直接回答,我想建議您避免通過XDocument和相關類直接處理Xml。

當處理xml(如果尚未提供.xsd文件)時,我傾向於通過創建其相應的.xsd來避免手動解析,並從中生成需要使用xml的類。

打開Visual Studio提示:

-> xsd "yourxml.xml" (a .xsd file will be generated) 
    -> xsd /c "yourxml.xsd" (a .cs file will be generated) 

(請注意,您可能需要手動調整生成的XSD,以更好地滿足您的需求和應用額外的約束上)

保存有關信息所需的類xml被生成。

現在,您可以使用.cs文件中生成的類讀取強類型對象中的整個xml。你只需要導入.cs文件在您的項目和反序列化原始的XML:

string fileContent = File.ReadAllText(fileLocation); 
var xmlObj = StringXmlSerializer.XmlDeserialize<YourXsdGeneratedType>(fileContent); 

您可以在內存中編輯XML和序列化回XML這樣的:

string xmlContext = StringXmlSerializer.XmlSerialize(xmlObj); 
File.WriteAllText(filePath, xmlObj); 

StringXmlSerializer是一個幫手我班我寫了適合我的需要序列化在內存中的字符串(但你可以直接在文件上序列化)。我張貼的代碼,讓你開始對:

/// <summary> 
/// Serialize object in xml format on a string 
/// </summary> 
public static class StringXmlSerializer 
{ 
    public static string XmlSerialize(object objectInstance) 
    { 
     XmlWriterSettings ws = new XmlWriterSettings(); 
     ws.NewLineHandling = NewLineHandling.Entitize; 

     var serializer = new XmlSerializer(objectInstance.GetType()); 
     var sb = new StringBuilder(); 

     using(XmlWriter xmlWriter = XmlWriter.Create(sb, ws)) 
      serializer.Serialize(xmlWriter, objectInstance); 

     return sb.ToString(); 
    } 

    public static T XmlDeserialize<T>(string objectData) 
    { 
     return (T)XmlDeserialize(objectData, typeof(T)); 
    } 

    public static object XmlDeserialize(string objectData, Type type) 
    { 
     var serializer = new XmlSerializer(type); 

     using(TextReader reader = new StringReader(objectData)) 
      return serializer.Deserialize(reader); 
    } 
} 

希望它可以幫助別人