使用Nokogiri解析XML但无法解析名称空间

时间:2015-02-17 10:59:50

标签: ruby xml curl nokogiri

我有一条XML消息,我的脚本需要能够解析“AccountStatus”节点元素。如果XML没有命名空间,我的脚本可以正常工作。

当我包含原始名称空间时,脚本执行Nokogiri::XML(request.body.read)时将丢失大量XML数据。

以下是XML示例:

curl -i -H -X POST -d "<?xml version="1.0" encoding="utf-8" ?>
<DocuSignEnvelopeInformation xmlns="http://www.w3.org/2001/XMLSchema">
    <EnvelopeStatus>
        <RecipientStatuses>
            <RecipientStatus>
                <Type>Signer</Type>
                <CustomFields />
                <AccountStatus>Active</AccountStatus>
                <RecipientId>ab2bf57b-72b7-48e7-8298-b1c7b56930b9</RecipientId>
            </RecipientStatus>
        </RecipientStatuses>
    </EnvelopeStatus>
</DocuSignEnvelopeInformation>" localhost:4567/shunt?uri=http://requestb.in/1hiag0y1

这是我的剧本:

require 'rubygems'
require 'sinatra'
require 'uri'
require 'nokogiri'
#require 'pry'
#require 'pp'

post '/shunt' do
puts "hello world"

xmldoc = Nokogiri::XML(request.body.read)
puts xmldoc

xmldoc.xpath("//docu:DocuSignEnvelopeInformation/EnvelopeStatus/RecipientStatuses/RecipientStatus", "docu"=>"http://www.w3.org/2001/XMLSchema").each do |node|
    puts node.text
end
end

这是输出:

hello world
<?xml version="1.0"?>
<DocuSignEnvelopeInformation/>

1 个答案:

答案 0 :(得分:1)

卷曲问题:

看起来您遇到的第一个问题是使用curl命令。请注意,您的curl命令指定了-H选项,但未指定标头。如果我尝试运行你的curl命令,我会收到一个错误:

curl: (6) Couldn't resolve host 'POST'

看起来这些报价搞砸了你的命令。在这个curl命令中:

curl -i -X POST -d "<?xml version="1.0" encoding="utf-8" ?> ...

...第一个引用的字符串是:

"<?xml version="

然后卷曲如何解释之后的一切 - 谁知道。您可以通过在xml外部使用单引号来解决该问题。但是,将xml放在文件中会更容易,然后让curl读取文件。如果您将-d选项与@一起使用,请执行以下操作:

 -d @file_name

curl将从名为file_name的文件中读取xml。这是一个例子:

curl -i -X POST -d @./xml_files/xml5.xml http://localhost:4567/shunt 

nokogiri问题:

xml中的每个子标记都是默认命名空间的一部分;因此,每个子标记名称前面都有命名空间名称。但是,您只将docu:放在xpath中第一个标记的前面:

//docu:DocuSignEnvelopeInformation/EnvelopeStatus/....
                                   ^
                                   |
                         missing namespace name

docu:必须在每个标记名称之前。另请注意,您可以在xpath中的每个标记前使用docu,而不是指定xmlns:命名空间。而且,写起来要简单得多:

  xpath = '//xmlns:AccountStatus'
  

供应商向我发送了一条XML消息

有关如何完成的详细信息非常重要。

这是一个curl命令,用于执行文件上传

curl -i -F "xmlfile=@xml5.xml" http://localhost:4567/shunt

-F表示发布文件上传。在本地,xml位于名为xml5.xml的文件中。然后,您的sinatra应用程序可以执行此操作:

post '/shunt' do
  require 'nokogiri'

  doc = Nokogiri::XML(
    params['xmlfile'][:tempfile].read
  )

  xpath = '//xmlns:AccountStatus'
  target_tag = doc.at_xpath(xpath)
  puts target_tag.text
end

或者,使用此curl命令:

curl -i -X POST -d @xml5.xml http://localhost:4567/shunt

...你的路线看起来像这样:

post '/shunt' do
  require 'nokogiri'

  doc = Nokogiri::XML(
    request.body.read
  )

  xpath = '//xmlns:AccountStatus'
  target_tag = doc.at_xpath(xpath)
  puts target_tag.text
end

内容类型标题:

-d选项将请求中的Content-Type标头设置为:

  

这将导致curl使用内容类型application / x-www-form-urlencoded将数据传递到服务器。

-F选项将请求中的Content-Type标头设置为:

  

这会导致curl使用Content-Type multipart / form-data

来POST数据