Сортировка XML-узлов по Alpha.Numeric с использованием C # - PullRequest
6 голосов
/ 06 января 2012

Скажем, у меня есть 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).

Ответы [ 5 ]

3 голосов
/ 06 января 2012

Вас заинтересовал ваш вопрос, так вот мои два цента.

Я реализовал 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));
}

Функциональное консольное приложение:

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 голосов
/ 06 января 2012

Очевидный ответ - да.

Если вы хотите получить такой результат:

<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 голосов
/ 06 января 2012

используя 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);
    }
}
1 голос
/ 06 января 2012

Еще одна попытка с использованием модифицированного Dotnet.Commons .Xml.

Получить класс XmlUtils здесь .

Создайте собственный 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)
0 голосов
/ 06 января 2012

Будет ли это решение от Java2S.com полезным?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...