2012-01-05 47 views
6

Nói rằng tôi có một XmlDocument mà tôi tạo ra có InnerXml trông như thế này:Nodes Sắp xếp XML bằng cách Alpha.Numeric sử dụng C#

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

Như bạn có thể thấy nút <PID.18> là trước khi nút <PID.3>. (<MSH.9> cũng trước <MSH.6>.)

Tái cấu trúc thế hệ của tôi sẽ làm cho mã sạch đẹp của tôi trở nên rất lộn xộn.

Có cách nào để sắp xếp các nút sao cho nó sẽ sắp xếp alpha cho đến khi nó chạm vào giai đoạn cuối cùng rồi sắp xếp số (nếu giá trị cuối cùng là số)?

Bằng cách "phân loại số" tôi có nghĩa là nó sẽ xem xét toàn bộ số thay vì char bằng char. (Vì vậy, 18> 3).

Trả lời

3

Câu trả lời rõ ràng là có.

Nếu đây là kết quả bạn muốn:

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

Sau đó, lớp này sẽ làm điều đó: (tôi nên được trả tiền cho việc này ...)

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

Ai đó có thể có khả năng viết này sạch hơn và meaner, nhưng điều này sẽ giúp bạn đi.

2

sử dụng System.Xml.Linq, mã này có thể giúp bạn.

Cách sử dụng:

string xmlString= 
    @" 
    ....your string..... 
    "; 

XDocument xDoc = XDocument.Load(new StringReader(xmlString)); 
XDocument newXDoc = SortXml(xDoc); 
Console.WriteLine(newXDoc); 

SortXml chức năng:

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

đã quan tâm đến câu hỏi của bạn vì vậy đây là hai xu của tôi.

Tôi đã triển khai IComparer<T> để xử lý so sánh phần tử và hai phương pháp xử lý đệ quy. Mã này có thể được dọn dẹp một chút nhưng tôi đã dán trong mã ứng dụng giao diện điều khiển mà tôi đã tạo để cho bạn thấy giải pháp của tôi mà tôi nghĩ là đã làm việc tốt.

Edit:Để thực hiện dễ dàng hơn này để đọc Tôi đã bị phá vỡ này xuống phần lõi dù tôi đã rời khỏi giao diện điều khiển ứng dụng chức năng

IComparer<T> Thực hiện:

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

Methods để xử lý đệ quy:

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

Functi Ứng dụng trên Bàn điều khiển:

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

Một lần thử khác, sử dụng sửa đổi Dotnet.Commons .Xml.

Tải lớp XmlUtils here.

Tạo một Comparer tùy chỉnh mà có tất cả các logic của bạn (điều này sẽ giúp bạn đi)

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

Sau đó thay đổi XmlUtils SortElements chức năng, thêm video này vào đầu:

CustomComparer Comparer = new CustomComparer ();

và thay đổi dòng:

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) 
Các vấn đề liên quan