基于日期时间属性 C#、XPath 对 XML 节点进行排序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/344737/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Sorting XML nodes based on DateTime attribute C#, XPath
提问by discorax
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. 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.
我试图找到一种方法来根据 DateTime.ToString() 值的 sTime 属性对节点进行排序。诀窍是我需要保持节点机智,但出于某种原因,我找不到办法做到这一点。我相当肯定 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 节点进行排序,然后将所有属性放入适当的数组中以供稍后使用。
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++;
}
采纳答案by jlew
There's an overload of XPathExpression.Addsort which takes an IComparer interface. If you implement the comparison yourself as IComparer, you could use this mechanism.
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 );
}
}
}
回答by Timothy Khouri
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.
这是一个完美运行的控制台应用程序。
回答by Robert Rossney
What you're trying to do is accomplished a lot more easily if the XML is properly constructed. The XML Schema recommendation says that date/time values should be represented in ISO8601 format, i.e. 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,那么您尝试做的事情会更容易完成。XML Schema 建议说日期/时间值应该以 ISO8601 格式表示,即CCCC-MM-DD HH:MM:SS
. (实际上 XML Schema 希望日期和时间之间的分隔符是 T,目前我不记得为什么。)
The two principal advantages of formatting dates and times this way are:
以这种方式格式化日期和时间的两个主要优点是:
- That's what other users of XML expect, and
- You can sort on their string values.
- 这就是 XML 的其他用户所期望的,并且
- 您可以对它们的字符串值进行排序。
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”格式说明符,它代表 - 等待 - “可排序”)。
回答by Dimitre Novatchev
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>
回答by Muneer
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
在日期时间只需替换我的日期时间格式中的额外字符我有额外的字符( - T : 和 + )所以只需替换它然后你的日期时间将是可以轻松排序的数字格式
回答by Chetan Mehra
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" />