【问题标题】:Problems inserting elements into XML fragment将元素插入 XML 片段的问题
【发布时间】:2015-10-05 20:45:28
【问题描述】:

基本问题实际上非常简单:我无法让 Nokogiri DocumentFragment 表现出预期的效果。它有两个节点,而不是如果它是一个实际的文档就会有一个节点,并且它不会将该节点识别为元素,而文档可以。

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

我正在尝试在我们的 Ruby on Rails 应用程序中使用名为 build_xml 的方法为对象构建 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 只有一个元素,没有第二个多余的节点。我可以尝试处理文档,然后在返回之前将其转换为片段,但我不知道生成的片段是否仍然存在问题,我更愿意了解发生了什么,所以我可以做对了就好了。

所以……

  1. 为什么片段不能将其唯一的真实节点识别为元素?我应该做些什么来强迫这个吗? Nokogiri 文档对元素节点的描述不多,但看起来它们没有可访问的属性来区分它们与通用节点。
  2. 为什么解析片段时会出现第二个空节点?
  3. 我只需要浏览完整的文档吗?有没有一种简单的方法可以将文档转换为片段?
  4. 我应该完全采用其他方式吗?

【问题讨论】:

  • 欢迎来到 Stack Overflow。真的很难想象你在描述什么。 Stack Overflow 需要输入 XML 的最小示例,以及演示问题的代码,以及所需输出的示例。请参阅“How to Ask”中的“帮助他人重现问题”部分。
  • 没有必要为丢失的信息道歉,但我们不能强调向我们提供必要信息的重要性。否则,我们会在黑暗中猜测和射击,这只会延迟为您提供有效的解决方案并使我们感到沮丧。此外,在添加附加信息时,请不要将其粘贴在问题的开头或结尾,并用“编辑”或“更新”标记。我们可以看到编辑/更新,并且添加的信息需要合并到您最初添加它的问题中,因此它是有意义的并且可以正确阅读。
  • 另外,请记住,代码和数据的最小示例比解释页面更有用。解释有助于定义意图,但代码向我们展示了您到达那里的路径。就像一张图片,例子值 1000 字。

标签: ruby-on-rails ruby xml nokogiri


【解决方案1】:

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

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

xml_fragment = Nokogiri::XML.fragment(xml_string)

不确定这是否真的是导致问题的原因,但它可能是一个开始的地方。

【讨论】:

  • 对不起,如果我很迟钝,但我在该页面上没有看到 tags 的定义。它是标签名称数组、每个标签内容为空的 XML 字符串、特定类型的标签对象还是其他?我希望这听起来并不苛刻或好战,我只是想填补文档中的空白(或我的阅读技能)。
  • 我尝试了frag = Nokogiri::XML.fragment(xml_string),而不是DocumentFragment.parse 方法,它返回一个片段对象,但该对象仍然有两个元素而不是一个,并且仍然无法将有意义的节点识别为一个元素(即frag.element_children 返回[])。
【解决方案2】:

虽然问题不清楚,但插入和删除节点的简要概述可能会有所帮助:

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"

找到&lt;bar&gt;节点并添加一个子节点:

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_xpathcssxpathat('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>

【讨论】:

  • 这就是问题的症结所在。在您的示例中,一旦您创建了名为 doc 的 fragment,它就有一个 foo 元素。要验证这一点,您可以执行doc.children.first.element?,它会返回true。但是,doc.elements 返回 []
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多