2016-09-01 72 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); 
    } 
} 

希望它可以帮助别人