У меня есть структура 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++;
}
Я использую веб-службу, и у меня есть вызов администратора, чтобы они добавили сортировку в хранимую процедуру, но если я не могу получить их, я должен попытаться найти решение с существующим Формат XML. Тем не менее, хорошее предложение.
Все эти ответы работают по-разному. Спасибо ребята. Все это полезно.
Почему, если не считать простого незнания, кто-то мог бы написать веб-сервис, который излучает XML, не имеющий схемы, которой нужно соответствовать? Это похоже на написание API с последующим его документированием с помощью заметки.





Есть перегрузка 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
Другая сложность заключается в том, что мне нужно отсортировать УЗЛЫ, а не только атрибуты.
Смотрите код. Вам нужно выбрать узлы, которые вы хотите отсортировать (узел элемента, а не атрибут sTime), и использовать выражение, представляющее выражение ключа сортировки (атрибут sTime) вместе с настраиваемым компаратором.
Ну вот:
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. Спасибо!
То, что вы пытаетесь сделать, будет намного проще, если XML будет правильно сконструирован. В рекомендации XML-схемы говорится, что значения даты / времени должны быть представлены в формате ISO8601, то есть CCCC-MM-DD HH:MM:SS. (На самом деле XML-схема хочет, чтобы разделитель между датой и временем был буквой T, и на данный момент я не помню, почему.)
Два основных преимущества такого форматирования даты и времени:
Жестко форматировать даты в XML любым другим способом, который будет обрабатываться XSLT.
Достаточно легко заставить .NET выдавать значения DateTime в этом формате (используйте спецификатор формата «s», который означает - подождите - «сортируемый»).
<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" />
Можете ли вы добавить еще один атрибут, который является сортируемой версией времени? Клещи или ГГГГММДД