【问题标题】:How can I sort XML entries with LibXML and Perl?如何使用 LibXML 和 Perl 对 XML 条目进行排序?
【发布时间】:2009-06-13 12:09:25
【问题描述】:

我正在使用LibXML 解析一个 XML 文件,并且需要按日期对条目进行排序。每个条目有两个日期字段,一个用于条目发布时间,一个用于更新时间。

<?xml version="1.0" encoding="utf-8"?>
...
<entry>
  <published>2009-04-10T18:51:04.696+02:00</published>
  <updated>2009-05-30T14:48:27.853+03:00</updated>
  <title>The title</title>
  <content>The content goes here</content>
</entry>
...

XML 文件已按更新日期排序,最新的在前。我可以轻松地将其反转以将较旧的条目放在首位:

my $parser = XML::LibXML->new();
my $doc = $parser->parse_file($file);
my $xc = XML::LibXML::XPathContext->new($doc->documentElement());

foreach my $entry (reverse($xc->findnodes('//entry'))) {
  ...
}

但是,我需要按发布日期对文件进行反向排序,而不是按更新日期。我怎样才能做到这一点?时间戳看起来也有点奇怪。我需要先对其进行标准化吗?

谢谢!

更新: 在摆弄 XPath 命名空间并失败后,我创建了一个函数来解析 XML 并将所需的值存储在哈希中。然后我使用了一个裸sort 对哈希进行排序,现在效果很好。

【问题讨论】:

  • 很高兴看到您选择了 XML::LibXML 路线。适应 XML 的严格性有点困难,但从长远来看,它会为您节省大量时间。 (我不得不与在实现 XML 方面走捷径的供应商合作,这意味着我实际上不能给他们 XML,我不得不给他们某种标签汤。非常烦人。)

标签: xml perl sorting libxml2


【解决方案1】:

一种方法是将您的 reverse 更改为 sort 语句(未经测试):

sub parse_date {
    # Transforms date from 2009-04-10T18:51:04.696+02:00 to 20090410
    my $date= shift;
    $date= join "", $date =~ m!\A(\d{4})-(\d{2})-(\d{2}).*!;
    return $date;
}

sub by_published_date {
    my $a_published= parse_date( $a->getChildrenByTagName('published') );
    my $b_published= parse_date( $b->getChildrenByTagName('published') );

    # putting $b_published in front will ensure the descending order.
    return $b_published <=> $a_published;
}

foreach my $entry ( sort by_published_date $xc->findnodes('//entry') ) {
    ...
}

希望这会有所帮助!

【讨论】:

  • 啊,我现在明白了,我想... $a 和 $b 是两个单独的条目,对吧?但是,我如何以编程方式浏览所有条目?有些文件有数百个条目...
  • $a 和 $b 由 sort 函数填写。对于列表中的任何两项,您的所有函数需要做的是,如果 $a 应该在 $b 之前排序,则返回 -1,如果 $b 应该在 $a 之前排序,则返回 1,否则返回 0。 sort 将处理其余部分。
  • 只是为了澄清(因为您问“如何以编程方式浏览所有条目”): sort 将多次调用您的函数,每次使用列表中的两个值(但没有特定顺序) .
  • 好吧...这几乎可以工作了,只是 xPath 比“已发布”要复杂一些——它是“./post:published”。我之前将命名空间声明为 $xc->registerNs(post => 'w3.org/2005/Atom');但是一旦对象被重铸为 $a 和 $b 它就会失去命名空间。有什么方法可以维护子内部的命名空间?
  • 更具体一点:$a$bsort 函数使用的全局变量。有关它们的更多信息,您可以阅读命令行上的perldoc -f sort,或perldoc.perl.org/functions/sort.html
【解决方案2】:

一个简单的排序可能会使来自不同时区的时间乱序:

 print for sort "2009-06-15T08:00:00+07:00", "2009-06-15T04:00:00+00:00";

这里,第二次是在第一次之后 3 小时,但先排序。

我不确定你所说的“古怪”是什么意思。您的示例仅以 rfc3339 格式显示时间戳。

【讨论】:

  • 啊。我认为这些时间戳是一些专有的东西,而不是实际的格式。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-10-25
  • 2017-10-24
  • 2011-07-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多