将元素插入XML片段的问题

时间:2015-10-05 20:45:28

标签: ruby-on-rails ruby xml nokogiri

基本问题实际上非常简单:我无法让Nokogiri DocumentFragment按预期运行。它有两个节点,而不是它是一个实际文档时所拥有的节点,并且它不会将该节点识别为元素,而文档则可以。

我需要一个片段而不是文档,因为我想将生成的XML作为元素插入到另一个文档(或片段)中。看起来我可能使用了错误的片段方法。

我试图在一个名为build_xml的方法中为Ruby on Rails应用程序中的对象构建XML表示。由于我有嵌套对象的层次结构,因此我将它作为一个将在类之间共享的通用方法,并在每个类中使用类常量来处理特定于类的信息。每个对象都创建一个Nokogiri DocumentFragment而不是一个完整的文档,这样任何返回的XML字符串都可以作为元素插入到包含对象的XML中。

我的问题是我无法通过片段向我展示其元素。所以,我有:

xml_string = self.to_xml({skip_types:true, skip_instruct: true})  # Use default to_xml method to get started
xml_fragment = Nokogiri::XML::DocumentFragment.parse(xml_string)  # Create Nokogiri doc fragment

此时,我想循环遍历每个嵌套对象并将其添加为片段唯一元素的子元素。但是,片段的element_children()方法返回一个空数组,而它的children()方法返回一个包含两个项目的数组,第一个是我想要的元素,第二个是一些文本对象,只包含换行符。

示例:

df = Datafile.first
xml_string = df.to_xml({skip_types:true, skip_instruct: true})
frag = Nokogiri::XML::DocumentFragment.parse(xml_string)
frag.element_children  # => returns []
frag.children  # => returns array of two children, one of which is datafile element, the other of which is just a linefeed.

如果我创建一个实际的XML文档而不仅仅是一个片段,那么该文档按预期填充了element_children,而且doc.children只有一个元素,没有第二个多余的节点。我可以尝试使用文档进行工作,然后在返回之前将其转换为片段,但我不知道结果片段是否仍然存在问题,我更愿意理解&#39继续,所以我可以做对,而不是。

所以......

  1. 为什么片段不会将其唯一的真实节点识别为元素?有什么我应该做的强制这个吗? Nokogiri文档没有详细描述元素节点,但看起来它们没有可访问的属性来区分它们与通用节点。
  2. 为什么在解析片段时会显示第二个空节点?
  3. 我只需要查看完整的文件吗?是否有一种简单的方法将文档转换为片段?
  4. 我应该完全以其他方式这样做吗?

3 个答案:

答案 0 :(得分:2)

您将整个XML字符串传递给parse,只需the tags as an argument

根据their docs,您应该做这样的事情:

xml_fragment = Nokogiri::XML.fragment(xml_string)

不确定这是否真的导致问题,但它可能是一个起点。

答案 1 :(得分:1)

虽然问题不明确,但是这个插入和删除节点的概述可能会有所帮助:

require 'nokogiri'

inserted_text = 'hello world!'

解析片段:

doc = Nokogiri::XML::DocumentFragment.parse('<foo><bar></bar></foo>')
doc.to_xml # => "<foo>\n  <bar/>\n</foo>"

将它与完整解析进行比较,该解析添加了XML声明:

doc = Nokogiri::XML('<foo><bar></bar></foo>')
doc.to_xml # => "<?xml version=\"1.0\"?>\n<foo>\n  <bar/>\n</foo>\n"

找到<bar>节点并添加子节点:

bar = doc.at('bar')
bar.children = "<baz a='1'>#{ inserted_text }</baz>"

doc.to_xml # => "<foo>\n  <bar>\n    <baz a=\"1\">hello world!</baz>\n  </bar>\n</foo>"

我使用at方法,找到第一个匹配的节点。它比search更具体,它将所有匹配的节点作为NodeSet返回,这类似于节点数组。两种方法都采用CSS或XPath选择器; CSS通常更容易阅读,但XPath具有更强大的功能,因此首先根据易读性和电源选择它们。 Nokogiri非常乐意在同一个剧本中使用它们。 atsearch分别具有CSS / XPath特定等效项:at_cssat_xpathcss以及xpathat('some_selector')相当于search('some_selector').first

另请注意,Nokogiri很乐意使用包含您要添加的XML或HTML的字符串。它会将其解析为片段,使您可以更轻松地定义要使用的内容。

这是如何轻松删除节点:

baz = doc.at('baz').remove

要更改节点的属性:

baz['a'] = 'hiya!'

将节点移动到其他地方:

doc.at('foo').add_child(baz)

这让我们将节点视为XML:

doc.to_xml # => "<foo>\n  <bar/>\n  <baz a=\"hiya!\">hello world!</baz>\n</foo>"

这让我们可以看到XML,就像我们正在查看文件一样:

puts doc.to_xml
# >> <foo>
# >>   <bar/>
# >>   <baz a="hiya!">hello world!</baz>
# >> </foo>

答案 2 :(得分:0)

嗯,解决方案只是更新Nokogiri的版本。据推测,这是在版本1.6.3.1和1.6.6.2之间修复的错误。