我有这个XML字符串:
xml = "<name>Married with Children</name>
<person age="20">Al Bundy</person>
<character age="20">Bud Bundy</character>
<character age="19">Marcy Darcy</character>
<person age="18">John Doe</person>"
我明白如果我使用
xml.css("characters")
它会生成一个带有字符标记的标题列表,类似于
[<character age="20">Bud Bundy</character>, <character age="19">Marcy Darcy</character>]
我只是想去掉name标签,所以我想:
[<person age="20">Al Bundy</person>,
<character age="20">Bud Bundy</character>,
<character age="19">Marcy Darcy</character>,
<person age="18">John Doe</person>]
是否有一种方法来创建一个人或字符标记的列表?
你的问题有很多问题,但我会试着把它们整理出来,这样你就能明白发生了什么,以及如何在将来写出更好的问题。
-
您的XML示例格式不正确:
xml = "<name>Married with Children</name> <person age="20">Al Bundy</person> <character age="20">Bud Bundy</character> <character age="19">Marcy Darcy</character> <person age="18">John Doe</person>" -:3: syntax error, unexpected tINTEGER, expecting end-of-input <person age="20">Al Bundy</person>
你得到这个是因为你有外部双引号,和双引号包装的参数。在提问时,确保样本数据是可用的是很重要的。它应该看起来像:
xml = '<name>Married with Children</name> <person age="20">Al Bundy</person> <character age="20">Bud Bundy</character> <character age="19">Marcy Darcy</character> <person age="18">John Doe</person>'
或者:
xml = <<EOT <name>Married with Children</name> <person age="20">Al Bundy</person> <character age="20">Bud Bundy</character> <character age="19">Marcy Darcy</character> <person age="18">John Doe</person> EOT
此时Ruby将允许您开始测试代码。
-
您的示例XML没有根节点。XML有严格的定义,因此,提供正确的数据非常重要:
require 'nokogiri' xml = '<name>Married with Children</name> <person age="20">Al Bundy</person> <character age="20">Bud Bundy</character> <character age="19">Marcy Darcy</character> <person age="18">John Doe</person>' doc = Nokogiri::XML(xml) doc.to_xml # => "<?xml version="1.0"?>n<name>Married with Children</name>n"
如果我使用
doc.errors
Nokogiri会告诉我为什么它只有一个节点:
有两种方法可以解决这个问题,添加一个换行根节点,或者告诉Nokogiri将标记视为文档的一个片段:doc.errors # => [#<Nokogiri::XML::SyntaxError: Extra content at the end of the document>]
doc = Nokogiri::XML('<root>' + xml + '</root>') doc.to_xml # => "<?xml version="1.0"?>n<root><name>Married with Children</name>n<person age="20">Al Bundy</person>n<character age="20">Bud Bundy</character>n<character age="19">Marcy Darcy</character>n<person age="18">John Doe</person></root>n"
或:
doc = Nokogiri::XML::DocumentFragment.parse(xml) doc.to_xml # => "<name>Married with Children</name>n<person age="20">Al Bundy</person>n<character age="20">Bud Bundy</character>n<character age="19">Marcy Darcy</character>n<person age="18">John Doe</person>"
请注意解析后两个dom之间的区别。第一个包含节点,第二个只包含样例XML中的节点。
既然DOM没有错误,就可以开始解析和收集数据了。如果您不能确保DOM被正确解析,那么您可能会发现Nokogiri对DOM进行了修正和修改,以使其在语法上正确,从而产生与您期望的不同的结构。太频繁地处理这些会让你发疯的。
-
在搜索时使用正确的标签名称。这是不言自明的,因为你要么得到一个结果,要么得到nil或一个空的NodeSet (
[]
):doc = Nokogiri::XML('<root>' + xml + '</root>') data = doc.css('characters') # => [] data.class # => Nokogiri::XML::NodeSet data = doc.at_css('characters') # => nil
而我们想要的是:
data = doc.css('character') # => [#<Nokogiri::XML::Element:0x3fc8c4c4d598 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fc8c4c4d4bc name="age" value="20">] children=[#<Nokogiri::XML::Text:0x3fc8c4c4c544 "Bud Bundy">]>, #<Nokogiri::XML::Element:0x3fc8c4c49d44 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fc8c4c49ce0 name="age" value="19">] children=[#<Nokogiri::XML::Text:0x3fc8c4c49830 "Marcy Darcy">]>] data = doc.at_css('character') # => #<Nokogiri::XML::Element:0x3fc8c4c4d598 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fc8c4c4d4bc name="age" value="20">] children=[#<Nokogiri::XML::Text:0x3fc8c4c4c544 "Bud Bundy">]>
或使用泛型方法:
data = doc.search('character') # => [#<Nokogiri::XML::Element:0x3fe8fe0771d8 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fe8fe076ff8 name="age" value="20">] children=[#<Nokogiri::XML::Text:0x3fe8fe07633c "Bud Bundy">]>, #<Nokogiri::XML::Element:0x3fe8fe07606c name="character" attributes=[#<Nokogiri::XML::Attr:0x3fe8fe073fd8 name="age" value="19">] children=[#<Nokogiri::XML::Text:0x3fe8fe073b50 "Marcy Darcy">]>] data = doc.at('character') # => #<Nokogiri::XML::Element:0x3fe8fe0771d8 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fe8fe076ff8 name="age" value="20">] children=[#<Nokogiri::XML::Text:0x3fe8fe07633c "Bud Bundy">]>
请注意,
at
及其at_css
和at_xpath
兄弟姐妹相当于search('for something').first
。
让我们来看看获取所需数据的一种方法:您可以使用CSS的,
操作符来查找多个不同的节点:
data = doc.search('character, person') # => [#<Nokogiri::XML::Element:0x3fd7de018c7c name="person" attributes=[#<Nokogiri::XML::Attr:0x3fd7de018b8c name="age" value="20">] children=[#<Nokogiri::XML::Text:0x3fd7de015b80 "Al Bundy">]>, #<Nokogiri::XML::Element:0x3fd7de014fb4 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fd7de014dd4 name="age" value="20">] children=[#<Nokogiri::XML::Text:0x3fd7de014550 "Bud Bundy">]>, #<Nokogiri::XML::Element:0x3fd7de014294 name="character" attributes=[#<Nokogiri::XML::Attr:0x3fd7de01421c name="age" value="19">] children=[#<Nokogiri::XML::Text:0x3fd7de011d00 "Marcy Darcy">]>, #<Nokogiri::XML::Element:0x3fd7de011a94 name="person" attributes=[#<Nokogiri::XML::Attr:0x3fd7de011a30 name="age" value="18">] children=[#<Nokogiri::XML::Text:0x3fd7de0112d8 "John Doe">]>]
data.map(&:to_xml) # => ["<person age="20">Al Bundy</person>", "<character age="20">Bud Bundy</character>", "<character age="19">Marcy Darcy</character>", "<person age="18">John Doe</person>"]
可以工作,但是您不能完全控制获得结果节点的顺序,而是按照它们在文档中出现的顺序。如果你想控制这个顺序,你可能会想要做两个单独的搜索,然后连接节点集。如何做那件事就留给你自己去想了。
要解析XML或HTML,很重要的是要理解CSS和/或XPath选择器。我建议专注于CSS选择器,因为它们通常更具可读性。此外,为了方便使用,Nokogiri实现了许多jQuery的CSS扩展,这增加了简单性的功能。XPath要强大得多,但代价是大量的视觉干扰。尽管如此,您还是希望熟悉它,以便在必要时从工具箱中取出该工具。
您可以使用简单的选择器,然后在Nokogiri中折叠/旋转/破坏结果,但是使用libXML的强大功能需要通过选择器向它提供信息,因此学习如何有效和正确地使用它们非常重要。让Nokogiri和libXML完成繁重的工作之间的速度差异将很快使您相信这一点。