如何让兄弟姐妹'孩子根据具体定义的兄弟内容

时间:2017-02-04 22:45:53

标签: ruby xml nokogiri

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

我不能根据他们的工作职位,作家,艺术家等来抓住合适的人。每个漫画书有时会有多位作家和艺术家。我的计划是将每个添加/附加到List。

所以,对于这本单一的漫画书,我需要让所有的作家和#39;和艺术家'显示名称,但作业功能(例如作家)是人名的兄弟。

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

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>

我的代码没有给我预期的结果。我发现&#34; Getting the siblings of a node with Nokogiri&#34;但我需要迭代并抓住每个兄弟姐妹。

我可以按<roleid>dfWriter</roleid><role id="dfWriter">Writer</role>进行搜索,因为它们是相同的。

我的预期输出是:

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

2 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

以下是我如何做到这一点:

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上使用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"]