2012-01-05 13 views
6

का उपयोग कर मैं एक 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> 

आप देख सकते हैं नोड <PID.18> नोड <PID.3> से पहले है। (<MSH.9><MSH.6> से पहले भी है।)

मेरी पीढ़ी के पुनर्गठन से मेरा अच्छा साफ कोड बहुत गन्दा हो जाएगा।

क्या नोड्स को सॉर्ट करने का कोई तरीका है ताकि यह अल्फा को सॉर्ट करेगा जब तक कि यह अंतिम अवधि को हिट न करे, फिर संख्यात्मक क्रमबद्ध करें (यदि अंतिम मान संख्याएं हैं)?

"न्यूमेरिक सॉर्टिंग" से मेरा मतलब है कि यह चार से चार की बजाय पूरे नंबर को देखेगा। (तो 18> 3)।

उत्तर

3

स्पष्ट उत्तर हाँ है।

इस परिणाम है आप चाहते हैं:

<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

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

अपने प्रश्न में दिलचस्पी थी इसलिए यहाँ मेरे दो सेंट है।

मैंने तत्व तुलना और दो विधियों को पुनरावृत्ति को संभालने के लिए IComparer<T> लागू किया है। कोड को थोड़ा साफ किया जा सकता है लेकिन मैंने आपके समाधान को दिखाने के लिए बनाए गए कंसोल एप्लिकेशन कोड में चिपकाया है जो मुझे लगता है कि अच्छी तरह से काम किया है।

संपादित करें:पढ़ने के लिए मैं मूल भागों में इस विभाजित कर दिया है इस आसान बनाने के लिए हालांकि मैं कार्यात्मक सांत्वना एप्लिकेशन

IComparer<T> कार्यान्वयन छोड़ दिया है:

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

Functi onal कंसोल आवेदन:

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

फिर भी एक और प्रयास, एक संशोधित Dotnet.Commons .xml का उपयोग कर।

XmlUtils क्लास here प्राप्त करें।

अपने सभी तर्क है कि एक कस्टम comparer बनाना होगा (यह मिल जाएगा तुम जा)

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 comparer = नए 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) 
संबंधित मुद्दे