如何在 Ruby 中按日期范围过滤 XML 个元素

How to filter XML elements by date range in Ruby

我通常使用 Nokogiri 作为我的 XML 解析器。

我有以下 XML:

<albums>
    <aldo_nova album="aldo nova">
        <release_date value="19820401"/>
    </aldo_nova>
    <classix_nouveaux album="Night People"/>
        <release_date value="19820501"/>
    </classix_nouveaux>
    <engligh_beat album="I Just Can't Stop It"/>
        <release_date value="19800501"/>
    </engligh_beat>
</albums>

我想获取在 1/1/1980 和 4/15/1982 之间发行的所有专辑:

<aldo_nova album="aldo nova">
    <release_date value="19820401"/>
</aldo_nova>
<engligh_beat album="I Just Can't Stop It"/>
    <release_date value="19800501"/>
</engligh_beat>

如何 filter/query XML release_date 范围?

您的 XML 格式不正确。解析后,Nokogiri 对此的评价如下:

doc.errors 
# => [#<Nokogiri::XML::SyntaxError: Opening and ending tag mismatch: albums line 1 and classix_nouveaux>,
#     #<Nokogiri::XML::SyntaxError: Extra content at the end of the document>]

那是因为:

<classix_nouveaux album="Night People"/>

<engligh_beat album="I Just Can't Stop It"/>

终止。相反,它们应该是:

<classix_nouveaux album="Night People">

<engligh_beat album="I Just Can't Stop It">

您可以使用 CSS 或 XPath 选择器来查找精确匹配,甚至是子字符串匹配,但 CSS 或 XPath 都无法理解 "ranges" 日期,它们也没有了解什么是日期,因此您必须提取所有节点,在本例中将日期值转换为日期对象或整数,然后与范围进行比较:

date_range = 19800501..19820401
selected_albums = doc.search('//release_date').select { |rd| date_range.include?(rd['value'].to_i) }.map { |rd| rd.parent }

selected_albums.map(&:to_xml)
# => ["<aldo_nova album=\"aldo nova\">\n" +
#    "    <release_date value=\"19820401\"/>\n" +
#    "</aldo_nova>",
#     "<engligh_beat album=\"I Just Can't Stop It\">\n" +
#    "    <release_date value=\"19800501\"/>\n" +
#    "</engligh_beat>"]

我认为您的 XML 设计不佳,因为您对应该是专辑的内容使用了不同的标签名称。 <album> 应该是 <albums> 的子代。我会推荐这样的东西:

<collection>
  <albums>
    <album band="aldo nova" title="aldo nova" release_date="19820401"/>
    <album band="classix nouveaux" title="Night People" release_date="19820501"/>
    <album band="english beat" title="I Just Can't Stop It" release_date="19800501"/>
  </albums>
</collection>

一旦 XML 成为标准格式,导航和搜索就会变得更容易:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<collection>
  <albums>
    <album band="aldo nova" title="aldo nova" release_date="19820401"/>
    <album band="classix nouveaux" title="Night People" release_date="19820501"/>
    <album band="english beat" title="I Just Can't Stop It" release_date="19800501"/>
  </albums>
</collection>
EOT

doc.search('album').last['title'] # => "I Just Can't Stop It"

band = 'aldo nova'
doc.search("//album[@band='#{band}']").map { |a| a['title'] } # => ["aldo nova"]

并且搜索日期变得更加直接,因为不需要找到节点的父节点:

date_range = 19800501..19820401
selected_albums = doc.search('album').select { |a| date_range.include?(a['release_date'].to_i) }
selected_albums.map(&:to_xml)
# => ["<album band=\"aldo nova\" title=\"aldo nova\" release_date=\"19820401\"/>",
#     "<album band=\"english beat\" title=\"I Just Can't Stop It\" release_date=\"19800501\"/>"]

我建议阅读一些关于 XML 本身的教程,因为如果数据未按逻辑正确表示,我们很容易陷入困境。