简体   繁体   English

使用LINQ排序

[英]Sorting with LINQ

I'm struggling with this sorting and need little bit help. 我正在努力进行这种排序,需要一些帮助。

I will sort child nodes along the rank and save it. 我将按排名对子节点进行排序并保存。 eg to order sub nodes inside the main element I will pass the ID for the specific section. 例如,为了命令主元素内的子节点,我将传递特定部分的ID。

This example works only for the first Descendants and now I stuck. 此示例仅适用于第一个后代,现在我卡住了。

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

I need like that. 我需要那样的。

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

Original 原版的

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

Target 目标

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

I think you have a typos there. 我觉得你有错字。 However, take a look at this solution: 但是,看看这个解决方案:

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

At this point x contains following XML: 此时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>

If att is there instead of 'opt` and should be included, following will work: 如果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)));

If you need to sort single element by name, use following (if no rank attribute exists or is empty, put on back): 如果需要按名称对单个元素进行排序,请使用以下内容(如果不存在任何排名属性或为空,则将其放在后面):

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

Even if this is not a direct answer to your problem, i wanna suggest you a way to avoid to deal directly with the Xml via XDocument and related classes. 即使这不是你问题的直接答案,我想建议你避免通过XDocument和相关类直接处理Xml。

When dealing with xml (if .xsd files are not already provided) I tend to avoid manual parsing by creating its corresponding .xsd and from that I generate the classes needed to work with the xml. 在处理xml时(如果还没有提供.xsd文件)我倾向于通过创建相应的.xsd来避免手动解析,并从中生成使用xml所需的类。

open visual studio prompt: 打开visual studio提示:

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

(Note that you may have to adjust the generated xsd manually to better fit your needs and apply additional contraints) (请注意,您可能需要手动调整生成的xsd以更好地满足您的需求并应用其他约束)

The class needed to hold information about the xml is generated. 生成了保存有关xml的信息所需的类。

Now you can read the whole xml in a strongly typed object, by using the class generated in the .cs file. 现在,您可以使用.cs文件中生成的类来读取强类型对象中的整个xml。 You just need to import the .cs file in your project and deserialize the original xml: 您只需要在项目中导入.cs文件并反序列化原始xml:

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

You can edit the xml in memory and serialize back to xml like this: 你可以编辑内存中的xml并像这样序列化回xml:

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

StringXmlSerializer is a helper i class i wrote that fit my needs to serialize in memory on a string (but you can serialize directly on a file). StringXmlSerializer是我写的一个帮助程序,它符合我在字符串内存序列化的需要(但你可以直接在文件上序列化)。 I post the code to get you started on that: 我发布了代码,让你开始:

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

Hope it helps someone 希望它可以帮助某人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM