简体   繁体   English

基于DateTime属性C#,XPath对XML节点进行排序

[英]Sorting XML nodes based on DateTime attribute C#, XPath

I have a XML Structure that looks like this. 我有一个看起来像这样的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>

I am trying to find a way to SORT the nodes based on the sTime attribute which is a DateTime.ToString() value. 我试图找到一种方法来根据sTime属性SORT节点,这是一个DateTime.ToString()值。 The trick is I need to keep the Nodes in tact and for some reason I can't find a way to do that. 诀窍是我需要保持节点的机智,由于某种原因我找不到办法做到这一点。 I'm fairly certain that LINQ and XPath have a way to do it, but I'm stuck because I can't seem to sort based on DateTime.ToString() value. 我很确定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;
    } 

I also need to maintain a pointer to the NODE to retrieve the values of the other attributes. 我还需要维护一个指向NODE的指针来检索其他属性的值。

Perhaps this isn't a simple as I thought it would be. 也许这并不像我想的那么简单。

Thanks. 谢谢。

Solution : Here's what I ended up using. 解决方案 :这是我最终使用的内容。 Taking the selected answer and the IComparable class this is how I get the XML nodes sorted based on the sTime attribute and then get the all the attributes into the appropriate Arrays to be used later. 获取所选答案和IComparable类,这就是我如何根据sTime属性对XML节点进行排序,然后将所有属性放入相应的Arrays中以便稍后使用。

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

      }

Here you go: 干得好:

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

That's a Console app that works perfectly. 这是一个完美运行的控制台应用程序。

There's an overload of XPathExpression.Addsort which takes an IComparer interface. XPathExpression.Addsort有一个重载,它接受一个I​​Comparer接口。 If you implement the comparison yourself as IComparer, you could use this mechanism. 如果您自己将比较实现为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 );
                }
            }
        }

Here is an XSLT solution: 这是一个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>

When this transformation is applied on the following XML document : 将此转换应用于以下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>

the correct result is produced: 产生了正确的结果:

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

What you're trying to do is accomplished a lot more easily if the XML is properly constructed. 如果正确构造XML,那么您正在尝试做的事情会轻松完成。 The XML Schema recommendation says that date/time values should be represented in ISO8601 format, ie CCCC-MM-DD HH:MM:SS . XML Schema建议说日期/时间值应以ISO8601格式表示,即CCCC-MM-DD HH:MM:SS (Actually XML Schema wants the separator between date and time to be a T, and at the moment I don't remember why.) (实际上,XML Schema希望日期和时间之间的分隔符为T,而目前我不记得为什么。)

The two principal advantages of formatting dates and times this way are: 以这种方式格式化日期和时间的两个主要优点是:

  • That's what other users of XML expect, and 这就是XML的其他用户所期望的
  • You can sort on their string values. 您可以对其字符串值进行排序。

It's a cruelty to format dates any other way in XML that's going to be processed by XSLT. 在XSLT处理的XML中以任何其他方式格式化日期是一种残酷。

It's easy enough to make .NET emit DateTime values in this format (use the "s" format specifier, which stands for - wait for it - "sortable"). 使.NET以这种格式发出DateTime值很容易(使用“s”格式说明符,代表 - 等待它 - “可排序”)。

suppose ur date time is in this format 假设你的日期时间是这种格式

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

then simplest way that can be done is 那么最简单的方法就是

< xsl:sort select="translate(XPATH_RETURNING_DATE,'-T:+','')" order="descending" data-type="number" /> <xsl:sort select =“translate(XPATH_RETURNING_DATE,' - T:+','')”order =“descending”data-type =“number”/>

IN DATETIME JUST REPLACE EXTRA CHARACTERS in my datetime format i have the extra characters ( - T : and + ) so just replace it AND THEN YOUR DATE TIME WILL BE IN NUMBER FORMAT THAT CAN BE SORTED EASILY 在DATETIME中我只能以我的日期时间格式替换额外的字符我有额外的字符( - T:和+)所以只需更换它,然后您的日期时间将是数字格式,可以轻松排序

I know this Question is quite old and you probably have a solution with you but i like to share my answer: 我知道这个问题很老,你可能有一个解决方案,但我想分享我的答案:

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

                //}
            }
        }
    }

Input XML is sample provided by @Dimitre Novatchev 输入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" />

Output 产量

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM