如何解析“< tag>”中的元素?

时间:2014-11-28 01:21:23

标签: ruby xml parsing

我有一个字符串:

string =  <RECALL>first_name</RECALL>, I'd like to send you something.  It'll help you learn more about both me and yourself.  What is your email?"

我想提取标记<RECALL>的值“first_name”。

我使用了gem crack,但它没有按照我的预期行事:

parsed = Crack::XML.parse(string) =>

{"RECALL"=>"first_name, I'd like to send you something.  It'll help you learn more about both me and yourself.  What is your email?"}

也许XML解析不是正确的方法。我有什么方法可以获得以下所需的行为呢?

{"RECALL"=>"first_name"}

2 个答案:

答案 0 :(得分:2)

对我来说看起来不像有效的XML。我会尝试在这里使用REGEXP:

string = "<RECALL>first_name</RECALL>, I'd like to send you something..."

/<RECALL>(.*)<\/RECALL>/.match(string)[1]
#=> "first_name"

答案 1 :(得分:0)

以下两种方法可以获取代码的内容:

string =  "<RECALL>first_name</RECALL>"

firstname = string[/<RECALL>([^<]+)</, 1]
firstname # => "first_name"

解析包含标签的字符串变得棘手。它适用于简单的内容,但一旦标记嵌套或出现其他<>,就会变得更加困难。

您可以使用XML解析器使用技巧:

require 'nokogiri'
string =  "foo <RECALL>first_name</RECALL> bar"

doc = Nokogiri::XML::DocumentFragment.parse(string)
doc.at('RECALL').text # => "first_name"

请注意,我使用Nokogiri::XML::DocumentFragment.parse。这告诉Nokogiri只期望部分XML文档,并放宽了许多通常严格的XML规则。然后我可以告诉解析器找到<RECALL>标记并获取其包含的文本。


  

...想知道是否有提取方法(我使用Crack来提取它,但只有当<tag>位于字符串末尾时它才有效。

此模式匹配mid-string:

str =  "foo <RECALL>first_name</RECALL> bar"
str[%r!<RECALL>([^<]+)</RECALL>!, 1] # => "first_name"

如果标签不在字符串的末尾,则此模式失败:

str[%r!<RECALL>([^<]+)</RECALL>\z!, 1] # => nil

如果它在字符串的末尾,则成功:

str =  "foo <RECALL>first_name</RECALL>"
str[%r!<RECALL>([^<]+)</RECALL>\z!, 1] # => "first_name"

这是一个正则表达式模式比使用解析器更容易做事的地方。

使用解析器:

require 'nokogiri'

通常我们不关心标签在DOM中的位置,但如果它很重要,我们可以弄清楚它与其他标签的关系。尽管如此,它永远不会那么简单:

如果标签不在字符串/ DOM的末尾,则返回nil

str =  "foo <RECALL>first_name</RECALL> bar"
doc = Nokogiri::XML::DocumentFragment.parse(str)
recall_node = doc.at('RECALL')
recall_node == doc.children.last ? doc.at('RECALL').text : nil # => nil

这将返回节点的文本,因为它位于DOM的末尾:

str =  "foo <RECALL>first_name</RECALL>"
doc = Nokogiri::XML::DocumentFragment.parse(str)
recall_node = doc.at('RECALL')
recall_node == doc.children.last ? doc.at('RECALL').text : nil # => "first_name"

这是有效的,因为文档中的每个节点都有一个标识符,我们可以询问感兴趣的节点是否与DOM中的最后一个节点匹配:

require 'nokogiri'

doc = Nokogiri::XML::DocumentFragment.parse("<node>first_name</node> text")
# => #(DocumentFragment:0x3ffc89c3d3e8 {
#      name = "#document-fragment",
#      children = [
#        #(Element:0x3ffc89c3cf9c {
#          name = "node",
#          children = [ #(Text "first_name")]
#          }),
#        #(Text " text")]
#      })
doc.at('node').object_id.to_s(16) # => "3ffc89c3cf9c"
doc.children.last.object_id.to_s(16) # => "3ffc89c3cec0"

doc = Nokogiri::XML::DocumentFragment.parse("<node>first_name</node>")
# => #(DocumentFragment:0x3ffc89c345cc {
#      name = "#document-fragment",
#      children = [
#        #(Element:0x3ffc89c342c0 {
#          name = "node",
#          children = [ #(Text "first_name")]
#          })]
#      })
doc.at('node').object_id.to_s(16) # => "3ffc89c342c0"
doc.children.last.object_id.to_s(16) # => "3ffc89c342c0"