【问题标题】:How to convert partial XML to hash in Ruby如何在 Ruby 中将部分 XML 转换为哈希
【发布时间】:2015-07-03 21:54:30
【问题描述】:

我有一个字符串,其中包含纯文本和额外的空格和回车符,然后是类似 XML 的标签,后跟 XML 标签:

String = "hi there.

<SET-TOPIC> INITIATE </SET-TOPIC>

<SETPROFILE>
   <KEY>name</KEY>
   <VALUE>Joe</VALUE>
</SETPROFILE>

 <SETPROFILE>
   <KEY>email</KEY>
   <VALUE>Email@hi.com</VALUE>
</SETPROFILE>

<GET-RELATIONS>
  <COLLECTION>goals</COLLECTION>
  <VALUE>walk upstairs</VALUE>
</GET-RELATIONS>
So what do you think?

Is it true?
 "

我想解析这个类似于使用 Nori 或 Nokogiri 或 Ox,它们将 XML 转换为哈希。

我的目标是能够轻松地将顶级标签作为键提取出来,然后了解所有元素,例如:

Keys = ['SETPROFILE', 'SETPROFILE', 'SET-TOPIC', 'GET-OBJECT']

Values[0] = [{name => Joe}, {email => email@hi.com}]
Values[3] = [{collection => goals}, {value => walk up}]

我已经看到了几个类似真正 XML 的函数,但我的所有函数都是部分的。

我开始沿着这条思路走:

parsed = doc.search('*').each_with_object({}) do |n, h| 
  (h[n.name] ||= []) << n.text 
end

【问题讨论】:

    标签: ruby xml xml-parsing nokogiri


    【解决方案1】:

    将字符串内容包装在一个节点中,您可以使用 Nokogiri 对其进行解析。 XML 段之外的文本将成为新节点中的文本节点。

    str = "hi there. .... Is it true?"
    doc = Nokogiri::XML("<wrapper>#{str}</wrapper>")
    segments = doc.xpath('/*/SETPROFILE')
    

    现在您可以使用“Convert a Nokogiri document to a Ruby Hash”将段转换为哈希。

    但是,如果纯文本包含一些需要在 XML 规范中转义的字符,您需要自己找到并转义它们。

    【讨论】:

    • 是否必须显式调出路径,例如SETPROFILE?我希望任何顶级节点都可以在一个数组中,例如 parsed.keys = ['SETPROFILE','SETPROFILE','GET-OBJECT','SET-TOPIC'] 然后节点中的值将是哈希.虽然在那种情况下,如果没有下面会发生什么?
    【解决方案2】:

    如果我想要 keysvalues 变量,我可能会按照这些思路做一些事情:

    require 'nokogiri'
    
    string = "hi there.
    
    <SET-TOPIC> INITIATE </SET-TOPIC>
    
    <SETPROFILE>
       <KEY>name</KEY>
       <VALUE>Joe</VALUE>
    </SETPROFILE>
    
     <SETPROFILE>
       <KEY>email</KEY>
       <VALUE>Email@hi.com</VALUE>
    </SETPROFILE>
    
    <GET-RELATIONS>
      <COLLECTION>goals</COLLECTION>
      <VALUE>walk upstairs</VALUE>
    </GET-RELATIONS>
    So what do you think?
    
    Is it true?
    "
    
    doc = Nokogiri::XML('<root>' + string + '</root>', nil, nil, Nokogiri::XML::ParseOptions::NOBLANKS)
    
    nodes = doc.root.children.reject { |n| n.is_a?(Nokogiri::XML::Text) }.map { |node| 
      [
        node.name, node.children.map { |c|
          [c.name, c.content]
        }.to_h
      ]
    }
    nodes
    # => [["SET-TOPIC", {"text"=>" INITIATE "}],
    #     ["SETPROFILE", {"KEY"=>"name", "VALUE"=>"Joe"}],
    #     ["SETPROFILE", {"KEY"=>"email", "VALUE"=>"Email@hi.com"}],
    #     ["GET-RELATIONS", {"COLLECTION"=>"goals", "VALUE"=>"walk upstairs"}]]
    

    nodes 可以获取其余的细节:

    keys = nodes.map(&:first)
    # => ["SET-TOPIC", "SETPROFILE", "SETPROFILE", "GET-RELATIONS"]
    
    values = nodes.map(&:last)
    # => [{"text"=>" INITIATE "},
    #     {"KEY"=>"name", "VALUE"=>"Joe"},
    #     {"KEY"=>"email", "VALUE"=>"Email@hi.com"},
    #     {"COLLECTION"=>"goals", "VALUE"=>"walk upstairs"}]
    
    values[0] # => {"text"=>" INITIATE "}
    

    如果您愿意,可以预处理 DOM 并删除顶级文本:

    doc.root.children.select { |n| n.is_a?(Nokogiri::XML::Text) }.map(&:remove)
    doc.to_xml
    # => "<root><SET-TOPIC> INITIATE </SET-TOPIC><SETPROFILE><KEY>name</KEY><VALUE>Joe</VALUE></SETPROFILE><SETPROFILE><KEY>email</KEY><VALUE>Email@hi.com</VALUE></SETPROFILE><GET-RELATIONS><COLLECTION>goals</COLLECTION><VALUE>walk upstairs</VALUE></GET-RELATIONS></root>\n"
    

    这使得使用 XML 变得更容易。

    【讨论】:

    • 最困难的部分是退后一步,找到获取所需数据块的最佳方法。这主要来自经验。
    猜你喜欢
    • 2010-12-16
    • 2016-08-26
    • 1970-01-01
    • 2011-12-19
    • 2011-05-08
    • 1970-01-01
    • 1970-01-01
    • 2013-07-08
    • 2010-12-11
    相关资源
    最近更新 更多