Сортировка XML-узлов по атрибуту DateTime C#, XPath

У меня есть структура XML, которая выглядит так.

<sales>
  <item name = "Games" sku = "MIC28306200" iCat = "28" 
     sTime = "11/26/2008 8:41:12 AM" 
     price = "1.00" desc = "Item Name" />
  <item name = "Games" sku = "MIC28307100" iCat = "28" 
     sTime = "11/26/2008 8:42:12 AM" 
     price = "1.00" desc = "Item Name" />
...
</sales>

Я пытаюсь найти способ СОРТИРОВАТЬ узлы на основе атрибута sTime, который является значением DateTime.ToString (). Уловка в том, что мне нужно держать узлы в тактическом состоянии, и по какой-то причине я не могу найти способ сделать это. Я почти уверен, что у LINQ и XPath есть способ сделать это, но я застрял, потому что не могу выполнить сортировку на основе значения DateTime.ToString ().

XPathDocument saleResults = new XPathDocument(@"temp/salesData.xml");
XPathNavigator navigator = saleResults.CreateNavigator();

XPathExpression selectExpression = navigator.Compile("sales/item/@sTime");
selectExpression.AddSort("@sTime", 
    XmlSortOrder.Descending, 
    XmlCaseOrder.None, 
    "", 
    XmlDataType.Number);

XPathNodeIterator nodeIterator = navigator.Select(selectExpression);

while( nodeIterator.MoveNext() )
    {
         string checkMe = nodeIterator.Current.Value;
    } 

Мне также нужно поддерживать указатель на УЗЕЛ, чтобы получать значения других атрибутов.

Возможно, это не так просто, как я думал.

Спасибо.

Решение: Вот что я в итоге использовал. Взяв выбранный ответ и класс IComparable, я получаю узлы XML, отсортированные на основе атрибута sTime, а затем помещаю все атрибуты в соответствующие массивы, которые будут использоваться позже.

    XPathDocument saleResults = new XPathDocument(@"temp/salesData.xml");
    XPathNavigator navigator = saleResults.CreateNavigator();
    XPathExpression selectExpression = navigator.Compile("sales/item");
    XPathExpression sortExpr = navigator.Compile("@sTime");
    selectExpression.AddSort(sortExpr, new DateTimeComparer());
    XPathNodeIterator nodeIterator = navigator.Select(selectExpression);
    int i = 0;
    while (nodeIterator.MoveNext())
       {
          if (nodeIterator.Current.MoveToFirstAttribute())
          {
              _iNameList.SetValue(nodeIterator.Current.Value, i);
          }
          if (nodeIterator.Current.MoveToNextAttribute())
          {
              _iSkuList.SetValue(nodeIterator.Current.Value, i);
          }
          ...
          nodeIterator.Current.MoveToParent();
          i++;

      }

Можете ли вы добавить еще один атрибут, который является сортируемой версией времени? Клещи или ГГГГММДД

Ryan Cook 05.12.2008 21:37

Я использую веб-службу, и у меня есть вызов администратора, чтобы они добавили сортировку в хранимую процедуру, но если я не могу получить их, я должен попытаться найти решение с существующим Формат XML. Тем не менее, хорошее предложение.

discorax 05.12.2008 21:43

Все эти ответы работают по-разному. Спасибо ребята. Все это полезно.

discorax 06.12.2008 00:43

Почему, если не считать простого незнания, кто-то мог бы написать веб-сервис, который излучает XML, не имеющий схемы, которой нужно соответствовать? Это похоже на написание API с последующим его документированием с помощью заметки.

Robert Rossney 06.12.2008 22:43
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
4
22 902
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Ответ принят как подходящий

Есть перегрузка XPathExpression.Addsort, которая принимает интерфейс IComparer. Если вы реализуете сравнение самостоятельно как IComparer, вы можете использовать этот механизм.

 class Program
        {
            static void Main(string[] args)
            {
                XPathDocument saleResults = new XPathDocument( @"salesData.xml" );
                XPathNavigator navigator = saleResults.CreateNavigator( );
                XPathExpression selectExpression = navigator.Compile( "sales/item" );
                XPathExpression sortExpr = navigator.Compile("@sTime");
                selectExpression.AddSort(sortExpr, new DateTimeComparer());
                XPathNodeIterator nodeIterator = navigator.Select( selectExpression );            
                while ( nodeIterator.MoveNext( ) )
                {
                    string checkMe = nodeIterator.Current.Value;
                }
            }
            public class DateTimeComparer : IComparer
            {
                public int Compare(object x, object y)
                {
                    DateTime dt1 = DateTime.Parse( x.ToString( ) );
                    DateTime dt2 = DateTime.Parse( y.ToString( ) );
                    return dt1.CompareTo( dt2 );
                }
            }
        }

Я пробовал это и получаю сообщение об ошибке Cannot convert type System.DateTime to System.Collections.IComparer

discorax 05.12.2008 21:41

Другая сложность заключается в том, что мне нужно отсортировать УЗЛЫ, а не только атрибуты.

discorax 05.12.2008 21:48

Смотрите код. Вам нужно выбрать узлы, которые вы хотите отсортировать (узел элемента, а не атрибут sTime), и использовать выражение, представляющее выражение ключа сортировки (атрибут sTime) вместе с настраиваемым компаратором.

jlew 05.12.2008 22:02

Ну вот:

XmlDocument myDoc = new XmlDocument();

myDoc.LoadXml(@"
<sales>
<item name = ""Games""
    sku = ""MIC28306200""
    iCat = ""28""
    sTime = ""11/26/2008 8:41:12 AM""
    price = ""1.00""
    desc = ""Item Name"" />
<item name = ""Games""
    sku = ""MIC28307100""
    iCat = ""28""
    sTime = ""11/26/2008 8:42:12 AM""
    price = ""1.00""
    desc = ""Item Name"" />
</sales>
");

var sortedItems = myDoc.GetElementsByTagName("item").OfType<XmlElement>()
    .OrderBy(item => DateTime.ParseExact(item.GetAttribute("sTime"), "MM/dd/yyyy h:mm:ss tt", null));

foreach (var item in sortedItems)
{
    Console.WriteLine(item.OuterXml);
}

Это консольное приложение, которое отлично работает.

Привет @ Тимоти. Кажется, ваш код делает то, что мне нужно. Не могли бы вы помочь мне с сообщением, которое я сделал ранее здесь: stackoverflow.com/questions/12943635/… Кроме того, я никогда не использовал Linq, поэтому я не уверен, будет ли ваш код работать в моей среде VB.NET. Спасибо!

Flo 18.10.2012 18:27

То, что вы пытаетесь сделать, будет намного проще, если XML будет правильно сконструирован. В рекомендации XML-схемы говорится, что значения даты / времени должны быть представлены в формате ISO8601, то есть CCCC-MM-DD HH:MM:SS. (На самом деле XML-схема хочет, чтобы разделитель между датой и временем был буквой T, и на данный момент я не помню, почему.)

Два основных преимущества такого форматирования даты и времени:

  • Это то, что ожидают другие пользователи XML, и
  • Вы можете отсортировать их строковые значения.

Жестко форматировать даты в XML любым другим способом, который будет обрабатываться XSLT.

Достаточно легко заставить .NET выдавать значения DateTime в этом формате (используйте спецификатор формата «s», который означает - подождите - «сортируемый»).

Вот решение XSLT:

<xsl:stylesheet version = "1.0"
 xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration = "yes" indent = "yes"/>

    <xsl:template match = "sales">
      <sales>
        <xsl:for-each select = "item">
          <xsl:sort select = "substring(@sTime,7,4)" data-type = "number"/>
          <xsl:sort select = "substring(@sTime,1,2)" data-type = "number"/>
          <xsl:sort select = "substring(@sTime,4,2)" data-type = "number"/>
          <xsl:sort select = "substring-after(substring-after(@sTime,' '),' ')" />
          <xsl:sort data-type = "number" select=
           "translate(
               substring-before(substring-after(@sTime,' '),' '),
               ':', ''
                      )
               " />
          <xsl:copy-of select = "."/>
        </xsl:for-each>
      </sales>
    </xsl:template>
</xsl:stylesheet>

Когда это преобразование применяется к следующему XML-документу:

<sales>
    <item name = "Games" sku = "MIC28306200" iCat = "28"
          sTime = "11/26/2008 8:41:12 PM"
          price = "1.00" desc = "Item Name" />
    <item name = "Games" sku = "MIC28307100" iCat = "28"
          sTime = "11/26/2008 8:42:12 AM"
                price = "1.00" desc = "Item Name" />
    <item name = "Games" sku = "MIC28307100" iCat = "28"
          sTime = "11/26/2008 11:42:12 AM"
                price = "1.00" desc = "Item Name" />
    <item name = "Games" sku = "MIC28306200" iCat = "28"
          sTime = "12/23/2008 8:41:12 PM"
          price = "1.00" desc = "Item Name" />
    <item name = "Games" sku = "MIC28307100" iCat = "28"
          sTime = "12/23/2008 8:42:12 AM"
                price = "1.00" desc = "Item Name" />
</sales>

получается правильный результат:

<sales>
   <item name = "Games" sku = "MIC28307100" iCat = "28" sTime = "11/26/2008 8:42:12 AM" price = "1.00" desc = "Item Name"/>
   <item name = "Games" sku = "MIC28307100" iCat = "28" sTime = "11/26/2008 11:42:12 AM" price = "1.00" desc = "Item Name"/>
   <item name = "Games" sku = "MIC28306200" iCat = "28" sTime = "11/26/2008 8:41:12 PM" price = "1.00" desc = "Item Name"/>
   <item name = "Games" sku = "MIC28307100" iCat = "28" sTime = "12/23/2008 8:42:12 AM" price = "1.00" desc = "Item Name"/>
   <item name = "Games" sku = "MIC28306200" iCat = "28" sTime = "12/23/2008 8:41:12 PM" price = "1.00" desc = "Item Name"/>
</sales>

Предположим, ваша дата и время в этом формате

2010-06-01T15: 16: 29 + 05: 00

тогда самый простой способ сделать это

<xsl: sort select = "translate (XPATH_RETURNING_DATE, '- T: +', '')" order = "убывание" data-type = "number" />

ВОВРЕМЯ ПРОСТО ЗАМЕНИТЕ ДОПОЛНИТЕЛЬНЫЕ ПЕРСОНАЖИ в моем формате даты и времени у меня есть дополнительные символы (- T: и +), поэтому просто замените их И ТОГДА ВАША ДАТА ВРЕМЯ БУДЕТ В ЧИСЛЕННОМ ФОРМАТЕ, КОТОРЫЙ МОЖНО ЛЕГКО СОРТИРОВАТЬ

Я знаю, что этот вопрос довольно старый, и у вас, вероятно, есть решение, но я хотел бы поделиться своим ответом:

      private static void  SortElementAttributesBasis(XmlNode rootNode)
    {



        for (int j = 0; j < rootNode.ChildNodes.Count; j++)
        {
            for (int i = 1; i < rootNode.ChildNodes.Count; i++)
            {
                Console.WriteLine(rootNode.OuterXml);
                DateTime dt1 = DateTime.ParseExact(rootNode.ChildNodes[i].Attributes["sTime"].Value, "M/d/yyyy h:mm:ss tt", System.Globalization.CultureInfo.InvariantCulture);
                DateTime dt2 = DateTime.ParseExact(rootNode.ChildNodes[i-1].Attributes["sTime"].Value, "M/d/yyyy h:mm:ss tt", System.Globalization.CultureInfo.InvariantCulture);
                int compare = DateTime.Compare(dt1,dt2);
                if (compare < 0)
                {
                    rootNode.InsertBefore(rootNode.ChildNodes[i], rootNode.ChildNodes[i - 1]);
                    Console.WriteLine(rootNode.OuterXml);
                }

                // Provide the name of Attribute in .Attribute["Name"] based on value you want to sort.

                   //if (String.Compare(rootNode.ChildNodes[i].Attributes["sTime"].Value, rootNode.ChildNodes[1 - 1].Attributes["sTime"].Value) < 0)
                //{
                //    rootNode.InsertBefore(rootNode.ChildNodes[i], rootNode.ChildNodes[i - 1]);

                //}
            }
        }
    }

Входной XML - образец, предоставленный @Dimitre Novatchev

<sales>
<item name = "Games" sku = "MIC28306200" iCat = "28"
      sTime = "11/26/2008 8:41:12 PM"
      price = "1.00" desc = "Item Name" />
<item name = "Games" sku = "MIC28307100" iCat = "28"
      sTime = "11/26/2008 8:42:12 AM"
            price = "1.00" desc = "Item Name" />
<item name = "Games" sku = "MIC28307100" iCat = "28"
      sTime = "11/26/2008 11:42:12 AM"
            price = "1.00" desc = "Item Name" />
<item name = "Games" sku = "MIC28306200" iCat = "28"
      sTime = "12/23/2008 8:41:12 PM"
      price = "1.00" desc = "Item Name" />
<item name = "Games" sku = "MIC28307100" iCat = "28"
      sTime = "12/23/2008 8:42:12 AM"
            price = "1.00" desc = "Item Name" />

Выход

<item name = "Games" sku = "MIC28307100" iCat = "28" sTime = "11/26/2008 8:42:12 AM" price = "1.00" desc = "Item Name" /><item name = "Games" sku = "MIC28307100" iCat = "28" sTime = "11/26/2008 11:42:12 AM" price = "1.00" desc = "Item Name" /><item name = "Games" sku = "MIC28306200" iCat = "28" sTime = "11/26/2008 8:41:12 PM" price = "1.00" desc = "Item Name" /><item name = "Games" sku = "MIC28307100" iCat = "28" sTime = "12/23/2008 8:42:12 AM" price = "1.00" desc = "Item Name" />

Другие вопросы по теме