使用 nokogiri 解析 xml 文件
Parse xml file with nokogiri
我需要在 rails 上解析 ruby 上的 xml 文件,我正在使用 nokogiri gem 来解析它。
我可以这样解析以显示 parent 和他的 children,但它的显示如下:
PARENT: Example Parent 1
CHILD: Example Children 1Example Children 2Example Children 3
PARENT: Example Parent 2
CHILD:
为什么它缺少第二个 parent 节点的 children?如果我用 for each 调用数组,它会显示所有 children。我是这样做的:
在控制器中:
@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 %>
我怎样才能"connect"和孩子们一起parent?介绍 parent 和他的 children,然后介绍其他 parent 和 children...
这是我的 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 个值。 .count
相当于最后一个索引 +1(对于只有 [0] .count
的数组是 1。您的 @parentN
被分配给 object
推荐(简单)
使用单个数组来保存嵌套值(作为散列)而不是两个变量。
#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')
用于selectparent节点,可以保持不变。我们希望将 @codes
键设置为嵌入在节点中的字符串的实际值,该值不是 parent.xpath('@name')
,而是 parent.xpath('@name')[0].value
。具有属性 'name' 的节点可能有多个 xml 表示,我们想要第一个 ([0]
) 表示。使用 .value
方法返回名称属性的值。
做一个class这样节点就变成objects
您的 Parent 节点有名称和颜色,您的 children 有名称、颜色和等级。看起来您的 Node 模型如下所示:
class Node
include ActiveModel::Model
attr_accessor :name, :color, :rank, :children
end
我在这里不使用持久性来简化事情,但您可能希望将记录保存到磁盘,如果您确实研究了 ActiveRecord 在 RailsGuides
上所做的一系列事情
现在,当我们浏览 xml 文档时,我们将创建一个 object 的数组,而不是字符串的散列(它们都恰好是 object,但是我会把这个问题留给你检查。
解析 Xpath 获取节点的属性 Objects
设置 parent 的名称和颜色属性的快速方法如下所示:
@node = Node.new(doc.xpath('Report/Node').first.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs })
好吧,也许这并不是那么容易。我们所做的是获取 XPath 的 Enumerable 结果,导航到第一个属性并生成字符串属性名称(名称、颜色、等级)及其对应值的散列。一旦我们有了哈希,我们就将它传递给我们的 Node class' 新方法来实例化(创建)一个节点。这将传递给我们一个 object 我们可以使用:
@node.name
#=> "Example Parent 1"
延长 Class 为 children
一旦我们有了 parent 节点,我们就可以给它 children,在数组中创建新节点。为了促进这一点,我们扩展了模型的定义以包含一个重写的初始化程序 (new())。
class Node
include ActiveModel::Model
attr_accessor :name, :color, :rank, :children
def initialize(*args)
self.children = []
super(*args)
end
end
添加 children
@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
创建一个节点 object 以及使用 .first
和前面的枚举创建它的 child ,我们就可以自动化这个过程。
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 %>
我需要在 rails 上解析 ruby 上的 xml 文件,我正在使用 nokogiri gem 来解析它。
我可以这样解析以显示 parent 和他的 children,但它的显示如下:
PARENT: Example Parent 1
CHILD: Example Children 1Example Children 2Example Children 3
PARENT: Example Parent 2
CHILD:
为什么它缺少第二个 parent 节点的 children?如果我用 for each 调用数组,它会显示所有 children。我是这样做的:
在控制器中:
@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 %>
我怎样才能"connect"和孩子们一起parent?介绍 parent 和他的 children,然后介绍其他 parent 和 children...
这是我的 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 个值。 .count
相当于最后一个索引 +1(对于只有 [0] .count
的数组是 1。您的 @parentN
被分配给 object
推荐(简单)
使用单个数组来保存嵌套值(作为散列)而不是两个变量。
#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')
用于selectparent节点,可以保持不变。我们希望将 @codes
键设置为嵌入在节点中的字符串的实际值,该值不是 parent.xpath('@name')
,而是 parent.xpath('@name')[0].value
。具有属性 'name' 的节点可能有多个 xml 表示,我们想要第一个 ([0]
) 表示。使用 .value
方法返回名称属性的值。
做一个class这样节点就变成objects
您的 Parent 节点有名称和颜色,您的 children 有名称、颜色和等级。看起来您的 Node 模型如下所示:
class Node
include ActiveModel::Model
attr_accessor :name, :color, :rank, :children
end
我在这里不使用持久性来简化事情,但您可能希望将记录保存到磁盘,如果您确实研究了 ActiveRecord 在 RailsGuides
上所做的一系列事情现在,当我们浏览 xml 文档时,我们将创建一个 object 的数组,而不是字符串的散列(它们都恰好是 object,但是我会把这个问题留给你检查。
解析 Xpath 获取节点的属性 Objects
设置 parent 的名称和颜色属性的快速方法如下所示:
@node = Node.new(doc.xpath('Report/Node').first.attributes.inject({}) { |attrs, value| attrs[value[0].to_sym] = value[1].value; attrs })
好吧,也许这并不是那么容易。我们所做的是获取 XPath 的 Enumerable 结果,导航到第一个属性并生成字符串属性名称(名称、颜色、等级)及其对应值的散列。一旦我们有了哈希,我们就将它传递给我们的 Node class' 新方法来实例化(创建)一个节点。这将传递给我们一个 object 我们可以使用:
@node.name
#=> "Example Parent 1"
延长 Class 为 children
一旦我们有了 parent 节点,我们就可以给它 children,在数组中创建新节点。为了促进这一点,我们扩展了模型的定义以包含一个重写的初始化程序 (new())。
class Node
include ActiveModel::Model
attr_accessor :name, :color, :rank, :children
def initialize(*args)
self.children = []
super(*args)
end
end
添加 children
@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
创建一个节点 object 以及使用 .first
和前面的枚举创建它的 child ,我们就可以自动化这个过程。
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 %>