【问题标题】:How to get siblings' child according to specific defined sibling content如何根据特定定义的兄弟内容获取兄弟姐妹的孩子
【发布时间】:2017-02-04 22:45:53
【问题描述】:

我需要找到从以下 XML 数据中收集作家和艺术家信息的最佳方法。 comic 节点出现多次,包含一本漫画书的数据。

我无法根据他们的工作职能,作家,艺术家等找到合适的人。有时每本漫画都有多个作家和艺术家。我的计划是将每个添加/附加到列表中。

因此,对于这本漫画书,我需要获取所有作家和艺术家的显示名称,但工作职能(例如作家)是人名的兄弟。

这是我拥有的,但不起作用:

writer = []
penciler = []
doc.xpath('//comic').each do |main_element|
 main_element.xpath("mainsection/credits/credit/role[@id='dfWriter']").each do |n|
    writer << n.xpath('person/displayname').text
  end
  main_element.xpath("mainsection/credits/credit/role[@id='dfPenciler']").each do |n|
    penciler << n.xpath('person/displayname').text
  end
end

p "Writer(s): ",writer
p "Penciler(s): ",penciler

这是 XML 文件/数据:

<comic>
  <id>3398</id>
  <index>195</index>
  <mainsection>
    <title>Mind Games</title>
    <myrating>0</myrating>
    <myrating>
      <displayname>0</displayname>
      <sortname>0</sortname>
    </myrating>
    <pagecount>32</pagecount>
    <credits>
      <credit>
        <role id="dfWriter">Writer</role>
        <roleid>dfWriter</roleid>
        <person>
          <displayname>Will Pfeifer</displayname>
          <sortname>Pfeifer, Will</sortname>
          <lastname>Pfeifer</lastname>
          <firstname>Will</firstname>
        </person>
      </credit>
      <credit>
        <role id="dfWriter">Writer</role>
        <roleid>dfWriter</roleid>
        <person>
          <displayname>John Byrne</displayname>
          <sortname>Byrne, John</sortname>
          <lastname>Byrne</lastname>
          <firstname>John</firstname>
        </person>
      </credit>
      <credit>
        <role id="dfPenciler">Penciller</role>
        <roleid>dfPenciler</roleid>
        <person>
          <displayname>John Byrne</displayname>
          <sortname>Byrne, John</sortname>
          <lastname>Byrne</lastname>
          <firstname>John</firstname>
        </person>
      </credit>
    </credits>
  </mainsection>
</comic>

我的代码没有给我想要的结果。我找到了“Getting the siblings of a node with Nokogiri”,但我需要迭代并抓取每个兄弟姐妹。

我可以通过&lt;roleid&gt;dfWriter&lt;/roleid&gt;&lt;role id="dfWriter"&gt;Writer&lt;/role&gt; 搜索,因为它们是相同的。

我的预期输出是:

Writer(s): Will Pfeifer, John Byrne 
Penciler(s): John Byrne

【问题讨论】:

  • XML 似乎与您的代码不一致。您将 comic 显示为根节点,但 //comic 不是我们搜索的方式。

标签: ruby xml nokogiri


【解决方案1】:

您可以使用 XPath following-sibling 轴来实现此目的,假设目标元素始终位于 role 之后:

doc.xpath('//comic').each do |main_element|
 main_element.xpath("mainsection/credits/credit/role[@id='dfWriter']").each do |n|
    writer << n.xpath('following-sibling::person/displayname').text
  end
  main_element.xpath("mainsection/credits/credit/role[@id='dfPenciler']").each do |n|
    penciler << n.xpath('following-sibling::person/displayname').text
  end
end

或者您可以首先迭代 credit 而不是 role

doc.xpath('//comic').each do |main_element|
 main_element.xpath("mainsection/credits/credit[role/@id='dfWriter']").each do |n|
    writer << n.xpath('person/displayname').text
  end
  main_element.xpath("mainsection/credits/credit[role/@id='dfPenciler']").each do |n|
    penciler << n.xpath('person/displayname').text
  end
end

【讨论】:

  • 非常感谢。这正是我一直在寻找的。对于此类问题,您有什么推荐的读物吗?
  • 学习 XPath。我从w3school 开始,然后是MDNspec。还有很多在 XPath 测试器中使用 XPath(例如 xpathtesterxpatheval
  • 我没有意识到 Xpath 有多牛。谢谢
  • Nokogiri 允许我们使用 XPath 和 CSS,这是有充分理由的。 CSS 更容易阅读,并且与处理 HTML 有很好的联系,但是 CSS 也缺乏 XPath 所具有的深度,因此学习两者都很好。我建议使用 CSS 以提高可读性,并且仅在必须具有特定功能时才跳转到 XPath。
【解决方案2】:

我会这样做:

require 'nokogiri'

XML = <<EOT
<comic>
  <mainsection>
    <credits>
      <credit>
        <role id="dfWriter">Writer</role>
        <person>
          <displayname>Will Pfeifer</displayname>
        </person>
      </credit>
      <credit>
        <role id="dfWriter">Writer</role>
        <person>
          <displayname>John Byrne</displayname>
        </person>
      </credit>
      <credit>
        <role id="dfPenciler">Penciller</role>
        <person>
          <displayname>John Byrne</displayname>
        </person>
      </credit>
    </credits>
  </mainsection>
</comic>
EOT

doc = Nokogiri::XML(XML)

writers = doc.search("credits role[id='dfWriter']").map { |w| w.parent.at('displayname').text }
pencilers = doc.search("credits role[id='dfPenciler']").map { |n| n.parent.at('displayname').text }

puts "Writer(s): %s" % writers.join(', ')
puts "Penciler(s): %s" % pencilers.join(', ')

# >> Writer(s): Will Pfeifer, John Byrne
# >> Penciler(s): John Byrne

运行时输出:

# >> Writer(s): Will Pfeifer, John Byrne
# >> Penciler(s): John Byrne

这个:

writers = doc.search("credits role[id='dfWriter']").map { |w| w.parent.at('displayname').text }
pencilers = doc.search("credits role[id='dfPenciler']").map { |n| n.parent.at('displayname').text }

可以干燥到:

writers, pencilers = %w(dfWriter dfPenciler).map { |s|
  doc.search("credits role[id='#{s}']").map { |w| w.parent.at('displayname').text }
}

我使用 CSS 来提高可读性,使用 at,它返回一个 Node,当我想要文本而不是 xpath,它返回一个 NodeSet。

在 NodeSet 和 Node 上使用 text 之间的区别非常重要。考虑一下:

require 'nokogiri'

xml = <<EOT
<root>
  <displayname>Will Pfeifer</displayname>
  <displayname>John Byrne</displayname>
  <displayname>John Byrne</displayname>
</root>
EOT

doc = Nokogiri::XML(xml)
doc.search('displayname').class # => Nokogiri::XML::NodeSet
doc.search('displayname').text # => "Will PfeiferJohn ByrneJohn Byrne"

doc.at('displayname').class # => Nokogiri::XML::Element
doc.at('displayname').text # => "Will Pfeifer"

如果您希望 NodeSet 的所有文本都以易于使用的形式出现,请从每个节点中提取它:

doc.search('displayname').map(&:text) # => ["Will Pfeifer", "John Byrne", "John Byrne"]

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 2019-08-29
    • 2019-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多