【问题标题】:Sort XML Nodes by Alpha.Numeric using C#使用 C# 按 Alpha.Numeric 对 XML 节点进行排序
【发布时间】:2012-01-05 20:41:17
【问题描述】:

假设我有一个XmlDocument,我生成的InnerXml 看起来像这样:

<ORM_O01>
  <MSH>
    <MSH.9>
      <MSG.2>O01</MSG.2>
    </MSH.9>
    <MSH.6>
      <HD.1>13702</HD.1>
    </MSH.6>
  </MSH>
  <ORM_O01.PATIENT>
   <PID>      
     <PID.18>
       <CX.1>SecondTestFin</CX.1>
     </PID.18>
     <PID.3>
        <CX.1>108</CX.1>
     </PID.3>
   </PID>
  </ORM_O01.PATIENT>
</ORM_O01>

如您所见,节点 &lt;PID.18&gt; 位于节点 &lt;PID.3&gt; 之前。 (&lt;MSH.9&gt;也在&lt;MSH.6&gt;之前。)

重组我的一代会导致我漂亮干净的代码变得非常混乱。

有没有办法对节点进行排序,以便它对 alpha 进行排序,直到到达最后一个句点,然后对数字进行排序(如果最后一个值是数字)?

通过“数字排序”,我的意思是它会查看整数而不是逐个字符。 (所以 18 > 3)。

【问题讨论】:

    标签: c# .net xml .net-4.0 xmldocument


    【解决方案1】:

    显而易见的答案是肯定的。

    如果这是你想要的结果:

    <ORM_O01>
      <MSH>
        <MSH.6>
          <HD.1>13702</HD.1>
        </MSH.6>
        <MSH.9>
          <MSG.2>O01</MSG.2>
        </MSH.9>
      </MSH>
      <ORM_O01.PATIENT>
        <PID>
          <PID.3>
            <CX.1>108</CX.1>
          </PID.3>
          <PID.18>
            <CX.1>SecondTestFin</CX.1>
          </PID.18>
        </PID>
      </ORM_O01.PATIENT>
    </ORM_O01>
    

    那么这门课就可以了:(我应该得到报酬……)

    using System;
    using System.IO;
    using System.Linq;
    using System.Xml.Linq;
    
    namespace Test
    {
        public class SortXmlFile
        {
            XElement rootNode;
    
            public SortXmlFile(FileInfo file)
            {
                if (file.Exists)
                    rootNode = XElement.Load(file.FullName);
                else
                    throw new FileNotFoundException(file.FullName);
            }
    
            public XElement SortFile()
            {
                SortElements(rootNode);
                return rootNode;
            }
    
            public void SortElements(XElement root)
            {
                bool sortWithNumeric = false;
                XElement[] children = root.Elements().ToArray();
                foreach (XElement child in children)
                {
                    string name;
                    int value;
                    // does any child need to be sorted by numeric?
                    if (!sortWithNumeric && Sortable(child, out name, out value))
                        sortWithNumeric = true;
                    child.Remove(); // we'll re-add it in the sort portion
                    // sorting child's children
                    SortElements(child);
                }
                // re-add children after sorting
    
                // sort by name portion, which is either the full name, 
                // or name that proceeds period that has a numeric value after the period.
                IOrderedEnumerable<XElement> childrenSortedByName = children
                        .OrderBy(child =>
                            {
                                string name;
                                int value;
                                Sortable(child, out name, out value);
                                return name;
                            });
                XElement[] sortedChildren;
                // if needed to sort numerically
                if (sortWithNumeric)
                {
                    sortedChildren = childrenSortedByName
                        .ThenBy(child =>
                            {
                                string name;
                                int value;
                                Sortable(child, out name, out value);
                                return value;
                            })
                            .ToArray();
                }
                else
                    sortedChildren = childrenSortedByName.ToArray();
    
                // re-add the sorted children
                foreach (XElement child in sortedChildren)
                    root.Add(child);
            }
    
            public bool Sortable(XElement node, out string name, out int value)
            {
                var dot = new char[] { '.' };
                name = node.Name.ToString();
                if (name.Contains("."))
                {
                    string[] parts = name.Split(dot);
                    if (Int32.TryParse(parts[1], out value))
                    {
                        name = parts[0];
                        return true;
                    }
                }
                value = -1;
                return false;
            }
        }
    }
    

    也许有人可以写得更简洁、更简洁,但这应该能让你继续前进。

    【讨论】:

      【解决方案2】:

      对你的问题很感兴趣,所以这是我的两分钱。

      我实现了IComparer&lt;T&gt; 来处理元素比较和两个处理递归的方法。代码可以稍微清理一下,但我已粘贴到我创建的控制台应用程序代码中,以向您展示我认为效果很好的解决方案。

      编辑: 为了便于阅读,尽管我已经离开了功能控制台应用程序,但我已将其分解为核心部分

      IComparer&lt;T&gt; 实现:

      public class SplitComparer : IComparer<string>
      {
          public int Compare(string x, string y)
          {
              var partsOfX = x.Split('.');
      
              int firstNumber;
              if (partsOfX.Length > 1 && int.TryParse(partsOfX[1], out firstNumber))
              {
                  var secondNumber = Convert.ToInt32(y.Split('.')[1]);
      
                  return firstNumber.CompareTo(secondNumber);
              }
      
              return x.CompareTo(y);
          }
      }
      

      处理递归的方法:

      private static XElement Sort(XElement element)
      {
          var xe = new XElement(element.Name, element.Elements().OrderBy(x => x.Name.ToString(), new SplitComparer()).Select(x => Sort(x)));
      
          if (!xe.HasElements)
          {
              xe.Value = element.Value;
          }
      
          return xe;
      }
      
      private static XDocument Sort(XDocument file)
      {
          return new XDocument(Sort(file.Root));
      }
      

      功能控制台应用程序:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      using System.Net;
      using System.IO;
      using System.Xml.Linq;
      
      namespace ConsoleApplication2
      {
          class Program
          {
              static void Main(string[] args)
              {
                  var xml = @"<ORM_O01>
                                <ORM_O01.PATIENT>
                                 <PID>      
                                   <PID.18>
                                     <CX.1>SecondTestFin</CX.1>
                                   </PID.18>
                                   <PID.3>
                                      <CX.1>108</CX.1>
                                   </PID.3>
                                 </PID>
                                </ORM_O01.PATIENT>
                                <MSH>
                                  <MSH.9>
                                    <MSG.2>O01</MSG.2>
                                  </MSH.9>
                                  <MSH.6>
                                    <HD.1>13702</HD.1>
                                  </MSH.6>
                                </MSH>
                              </ORM_O01>";
      
                  var xDoc = XDocument.Parse(xml);
      
                  var result = Sort(xDoc);
      
                  Console.WriteLine(result.ToString());
      
                  Console.Read();
              }
      
              private static XElement Sort(XElement element)
              {
                  var xe = new XElement(element.Name, element.Elements().OrderBy(x => x.Name.ToString(), new SplitComparer()).Select(x => Sort(x)));
      
                  if (!xe.HasElements)
                  {
                      xe.Value = element.Value;
                  }
      
                  return xe;
              }
      
              private static XDocument Sort(XDocument file)
              {
                  return new XDocument(Sort(file.Root));
              }
          }
      
          public class SplitComparer : IComparer<string>
          {
              public int Compare(string x, string y)
              {
                  var partsOfX = x.Split('.');
      
                  int firstNumber;
                  if (partsOfX.Length > 1 && int.TryParse(partsOfX[1], out firstNumber))
                  {
                      var secondNumber = Convert.ToInt32(y.Split('.')[1]);
      
                      return firstNumber.CompareTo(secondNumber);
                  }
      
                  return x.CompareTo(y);
              }
          }
      }
      

      【讨论】:

        【解决方案3】:

        使用System.Xml.Linq,此代码可能会对您有所帮助。

        用法:

        string xmlString=
            @"
            ....your string.....
            ";
        
        XDocument xDoc = XDocument.Load(new StringReader(xmlString));
        XDocument newXDoc = SortXml(xDoc);
        Console.WriteLine(newXDoc);
        

        SortXml函数:

        XDocument SortXml(XDocument xDoc)
        {
            Func<XElement, string> keyBuilder = 
               s => s.Name.ToString().Split('.')
                     .Aggregate("",(sum, str) => sum += str.PadLeft(32,' '));
        
            XElement root = new XElement(xDoc.Root.Name);
            SortXml(root, xDoc.Elements(), keyBuilder);
            return new XDocument(root);
        }
        
        void SortXml(XElement newXDoc, IEnumerable<XElement> elems, Func<XElement, string> keyBuilder)
        {
            foreach (var newElem in elems.OrderBy(e => keyBuilder(e)))
            {
                XElement t = new XElement(newElem);
                t.RemoveNodes();
                newXDoc.Add(t);
                SortXml(t, newElem.Elements(), keyBuilder);
            }
        }
        

        【讨论】:

          【解决方案4】:

          又一次尝试,使用修改后的Dotnet.Commons.Xml。

          获取 XmlUtils 类here

          创建一个包含所有逻辑的自定义比较器(这会让你继续前进)

          public class CustomComparer : IComparer
          {
              public int Compare(object x, object y)
              {
                  string o1 = x as string;
                  string o2 = y as string;
          
                  string[] parts1 = o1.Split('.');
                  string[] parts2 = o2.Split('.');
          
                  // Assuming first part is alpha, last part is numeric and both of them has second part. Otherwise compare original ones.
                  if (parts1.Length < 2 || parts2.Length < 2)
                      return o1.CompareTo(o2);
          
                  if (parts1[0].Equals(parts2[0]))
                  {
                      // Do a numeric compare
                      return int.Parse(parts1[parts1.Length - 1]).CompareTo(int.Parse(parts2[parts2.Length - 1]));
                  }
                  else
                  {
                      // Just compare the first part
                      return parts1[0].CompareTo(parts2[0]);
                  }
              }
          

          然后修改XmlUtils的SortElements函数,把这个加到最上面:

          CustomComparer 比较器 = new CustomComparer();

          然后换行:

          if (String.Compare(node.ChildNodes[i].Name, node.ChildNodes[i-1].Name, true) < 0)
          

          if (comparer.Compare(node.ChildNodes[i].Name, node.ChildNodes[i - 1].Name) < 0)
          

          【讨论】:

            【解决方案5】:

            这个solution from Java2S.com 有用吗?

            【讨论】:

              猜你喜欢
              • 2013-08-30
              • 2020-08-14
              • 2023-01-11
              • 1970-01-01
              • 2021-01-16
              • 1970-01-01
              • 2017-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多