从XML文档中进行XPath排序

4

我有一个XML文档,其中包含一些日期数据,例如:

<Message>
<messagetext>Testing purpose only</messagetext>
<date>05.02.2010</date>
</Message>

我希望按照XPath键对它们进行排序,我应该如何做到呢?

1
日期时间格式是 MM.dd.yyyy 还是 dd.MM.yyyy? - AnthonyWJones
你不想使用Linq to XML的原因是什么?相比XPath,代码通常更容易理解。 - Jarrett Widman
你是想按排序顺序仅读取数据,还是想修改文档并再次保存? - Mark Byers
只需要按照排序顺序读取数据。 - İbrahim Akgün
4个回答

3
XPathExpression类允许您添加排序规则。以下是一些在.NET 2.0中应该有效的示例代码:
XPathDocument doc = new XPathDocument(@"..\..\XMLFile1.xml");
XPathNavigator nav = doc.CreateNavigator();
XPathExpression exp = nav.Compile("Messages/Message");
exp.AddSort(
  "number(concat(substring(date, 7), substring(date, 4, 2), substring(date, 1, 2)))",
  XmlSortOrder.Descending, 
  XmlCaseOrder.None, 
  null, 
  XmlDataType.Number
);
foreach (XPathNavigator msg in nav.Select(exp))
{
  Console.WriteLine(
    "{0}: {1}", 
    msg.SelectSingleNode("date").Value, 
    msg.SelectSingleNode("messagetext").Value
  );
}

将XMLFile1.xml作为

<Messages>
  <Message>
    <messagetext>Message 2</messagetext>
    <date>04.02.2010</date>
  </Message>
  <Message>
    <messagetext>Message 1</messagetext>
    <date>05.02.2010</date>
  </Message>
  <Message>
    <messagetext>Message 3</messagetext>
    <date>05.02.2009</date>
  </Message>
</Messages>

输出结果为:
05.02.2010: Message 1
04.02.2010: Message 2
05.02.2009: Message 3

假定的日期格式是ddmmyyyy,但如果你想要mmddyyyy,你可以根据需要更改这些子字符串表达式。


+1 嘿,马丁,欢迎来到 StackOverflow,很高兴在这里见到你。你最近在哪里呀?我早就期待着看到你出现在这里,带着你优秀的 XML 答案。;) - AnthonyWJones

1
您可以使用LINQ to XML来实现。我假设您正在使用格式MM.dd.yyyy,但如果您想要更改,这很容易实现:
using System;
using System.Linq;
using System.Xml.Linq;

public class Message
{
    public string Text { get; set; }
    public DateTime Date { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        XDocument doc = XDocument.Load("input.xml");
        var messages = doc.Descendants("Message")
            .Select(element => new Message
            {
                Text = element.Element("messagetext").Value,
                Date = DateTime.ParseExact(element.Element("date").Value, "MM.dd.yyyy", null)
            }).OrderBy(message => message.Date);
        foreach (Message message in messages)
        {
            Console.WriteLine("{0} : {1}", message.Date, message.Text);
        }
    }
}

结果:

02-05-2010 00:00:00 : Test1
17-05-2010 00:00:00 : Test2
22-05-2010 00:00:00 : Test3

我使用的测试数据:

<xml>
  <Message>
    <messagetext>Test1</messagetext>
    <date>05.02.2010</date>
  </Message>
  <Message>
    <messagetext>Test3</messagetext>
    <date>05.22.2010</date>
  </Message>
  <Message>
    <messagetext>Test2</messagetext>
    <date>05.17.2010</date>
  </Message>
</xml>

我在服务器上安装了.NET 2.0,所以无法使用LINQ。 - İbrahim Akgün

0
首先,XPath除了作为XSLT转换的一部分外,并不能帮助进行任何排序。其次,在.NET中使用的XPath 1.0并不具备任何日期特定的支持。
最简单的方法是将XML加载到XDocument中,并使用以下代码(假设文档中的顶级根节点下有一系列"Message"节点):
Func<XElement, DateTime> fn = e => DateTime.ParseExact(e.Element("date").Value, "dd.MM.yyyy", CultureInfo.InvariantCulture);

var messages = doc.Root.Elements("Message").OrderBy(fn);

foreach (var elem in messages)
{
    Console.WriteLine(fn(elem));
}

另外,如果您有理由坚持使用XmlDocument,则这段代码略显臃肿但依然可用:

Func<XmlElement, DateTime> fn = e => DateTime.ParseExact(e.SelectSingleNode("date").InnerText, "dd.MM.yyyy", CultureInfo.InvariantCulture);

var messages = doc.DocumentElement.SelectNodes("Message")
    .Cast<XmlElement>().OrderBy(fn);


foreach (var elem in messages)
{
    Console.WriteLine(fn(elem));
}

我在我的服务器上安装了.NET 2.0,所以无法使用LINQ。 - İbrahim Akgün
@Ibrahim:Windows 2000?还是有其他问题导致您无法安装3.5? - AnthonyWJones
我无法访问和安装任何东西。服务器是Windows 2008 :) - İbrahim Akgün
@Ibrahim:如果它是Windows 2008,则具有.NET 3.5。您可能认为它仅具有.NET 2.0,因为应用程序池.NET Framework版本没有3.5选项。但是,此字段名称错误(在我看来),它应该说.NET CLR版本。.NET 3.5仍然使用2.0 CLR。在Windows 2008框中,您应该发现已经存在.NET 3.5。 - AnthonyWJones
谢谢,我会查看它。然后我会回答你。 - İbrahim Akgün

0
如果您可以控制XML的格式,我建议将日期格式更改为ISO 8601。这样,日期可以像常规字符串一样进行排序。例如,05.02.2010将变成2010-05-02。此外,ISO 8601不那么含糊(月份在日之前还是日在月份之前?)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接