我有一条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/>
旋度问题:
看起来您遇到的第一个问题是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="
然后curl是如何解释这之后的一切的——谁知道呢。您可以通过在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中的每个标记前面使用xmlns:
,而不是指定docu
命名空间。而且,只写要简单得多
xpath = '//xmlns:AccountStatus'
供应商给我发了一条XML消息
如何做到这一点的细节很重要。
这里有一个curl命令,它将执行文件上传:
curl -i -F "xmlfile=@xml5.xml" http://localhost:4567/shunt
-F
表示POST文件上传。在本地,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
选项将请求中的内容类型标头设置为:
这将导致curl使用内容类型application/x-www-form-urlencoded将数据传递给服务器。
-F
选项将请求中的内容类型标头设置为:
这导致curl使用内容类型多部分/表单数据来POST数据