使用nokogiri分析xml文件



我需要在ruby on rails上解析一个xml文件,我使用nokogiri gem来解析它。

我可以这样解析以显示家长和他的孩子,但它看起来是这样的:

PARENT: Example Parent 1
CHILD: Example Children 1Example Children 2Example Children 3
PARENT: Example Parent 2
CHILD:

为什么它缺少第二个父节点的子节点?如果我用一个for each调用数组,它将显示所有子项。我是这样做的:

在控制器中:

  @codes = []
    doc.xpath('//Node').each do |parent| 
       @parentN =parent.xpath('///ancestor::*/@name')

      @codes << parent.xpath('Node/@name').text
    end

和观点:

<% for x in 0...@parentN.count %>
    <p> PARENT: <%= @parentN[x]  %>  </p>
  <p> CHILD:  <%= @codes[x] %>  </p>
    <%   end %>

如何将父母与子女"联系起来"?呈现父母和他的孩子,然后是其他父母和孩子。。。

这是我的xml文件:

   <Report>
       <Node name="Example Parent 1" color="red">
          <Node name="Example Children 1" color="red" rank="very high" />
          <Node name="Example Children 2" color="red" rank="high" />
          <Node name="Example Children 3" color="yellow" rank="moderate" />
       </Node>
       <Node name="Example Parent 2" color="yellow">
          <Node name="Example Children 1" color="yellow" rank="moderate" />
       </Node>
    </Report>

问题#1

在行中:

       @parentN =parent.xpath('///ancestor::*/@name')

您可以覆盖@parentN的上一个值。

问题#2

通过运行

<% for x in 0...@parentN.count %>

对于一个单值数组,您将获得2个值。CCD_ 2等价于最后一个索引+1(对于只有[0] .count的数组。您的@parentN已分配给对象

建议(简单)

使用单个数组来保存嵌套值(作为哈希),而不是两个变量。

#xmlController.rb
@codes = []
doc.xpath('Report/Node').each do |parent| 
  @codes << { parent.xpath('@name') => parent.xpath('Node').map { |child| child.text }
end

#show.html.erb
<% @codes.each do |parent, children| %>
  <p> PARENT: <%= @parent  %>  </p>
  <p> CHILDREN:  <%= @children.each { |child| p child } %>  </p>

基于以下评论的建议

上面展示了思考这个问题的最简单的方法。现在我们已经准备好解析节点中的所有数据了,我们需要更改我们的xpath和映射。doc.xpath('Report/Node')用于选择父节点,并且可以保持不变。我们希望将@codes键设置为嵌入Node中的字符串的实际值,该字符串不是parent.xpath('@name'),而是实际的parent.xpath('@name')[0].value。可能有多个具有属性"name"的节点的xml表示,我们想要第一个([0])。name属性的值使用.value方法返回。

创建一个类,使节点成为对象

父节点有名称和颜色,子节点有名称、颜色和级别。看起来你有一个Node的模型,看起来像:

class Node
  include ActiveModel::Model
  attr_accessor :name, :color, :rank, :children
end

我在这里不使用持久性来简化事情,但你可能想把你的记录保存到磁盘上,如果你确实研究了ActiveRecord在RailsGuides 上所做的大量事情

现在,当我们浏览xml文档时,我们将创建一个对象数组,而不是字符串散列(这两个对象恰好都是对象,但我将把这个问题留给您查看)。

解析Xpath以获取节点对象的属性

设置父对象的名称和颜色属性的快速方法如下所示:

@node = Node.new(doc.xpath('Report/Node').first.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs })

好吧,也许这并不那么容易。我们所做的是获取XPath的可枚举结果,导航到第一个属性,并对字符串属性名称(名称、颜色、排名)及其相应值进行散列。一旦我们有了散列,我们就把它传递给Node类的新方法来实例化(创建)一个节点。这将传递给我们一个我们可以使用的对象:

@node.name
#=> "Example Parent 1"

为孩子们扩展课堂

一旦我们有了父节点,我们就可以给它子节点,在数组中创建新节点。为了便于实现这一点,我们扩展了模型的定义,以包括一个重写的初始值设定项(new())。

class Node
  include ActiveModel::Model
  attr_accessor :name, :color, :rank, :children
  def initialize(*args)
    self.children = []
    super(*args)
  end
end
添加子项
@node.children << Node.new(doc.xpath('Report/Node').first.xpath('Node').first.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs })

既然我们知道了如何使用.first创建Node对象,并使用.first创建它的子对象和前面的枚举,我们就可以自动化这个过程了。

doc.xpath('Report/Node').each do |parent|
  node = Node.new(parent.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs }))
  node.children = parent.xpath('Node').map do |child|
    Node.new(child.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs }))
  end
end

丑陋的控制器代码

将其移动到模型

但是等等!那不是很干!让我们把伤害我们眼睛的逻辑转移到模型中,使其更容易使用。

class Node
  include ActiveModel::Model
  attr_accessor :name, :color, :rank, :children
  def initialize(*args)
    self.children = []
    super(*args)
  end
  def self.new_from_xpath(xml_node)
    self.new(xml_node.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs })
  end
end

最终控制器

现在控制器看起来是这样的:

@nodes = []
doc.xpath('Report/Node').each do |parent|
  node = Node.new_from_xpath(parent)
  node.children = parent.xpath('Node').map do |child|
    Node.new_from_xpath(child)
  end
  @nodes << node
end

在视图中使用此

在视图中,您可以使用@nodes,如下所示:

<% for @node in @nodes %>
  Parent: <%= @node.name %>
  Children: <% for @child in @node.children %>
    <%= @child.name %> is <%= @child.color %>
  <% end %>
<% end %>

相关内容

  • 没有找到相关文章

最新更新