如何从 savon 响应中解析属性

How to parse attribute from savon response

我正在底部发布我正在使用的 soap 响应。
我需要从 <t:Body BodyType="HTML">

中获取 BodyType="HTML" 属性

response.body 把整个事情变成一个散列,里面没有 BodyType="HTML" 的迹象。

执行 response.doc.css("t|Body") 会生成错误:Undefined namespace prefix: //t:Body (Nokogiri::XML::XPath::SyntaxError) 因为我在 XML.

中没有看到该命名空间声明

正在做 response.doc.css("Body") return 空白。

我怎样才能检索 BodyType 的值?

由于发布发出 secure/private soap 请求的代码没有意义,我将发布一些从平面文件中读取 XML 的基本代码:

require 'savon'
require 'active_support/core_ext/hash/conversions'
require 'nokogiri'

@doc = Nokogiri::XML(File.open("tmp.xml"))
puts @doc.css("t|Body")

这里是 XML:

<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" MajorVersion="15" MinorVersion="1" MajorBuildNumber="629" MinorBuildNumber="8" Version="V2016_07_13"/>
  </s:Header>
  <s:Body>
    <m:GetItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:GetItemResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:Items>
            <t:Message>
              <t:ItemId Id="AAMkADE2NjQyMjVlLWNhY2UtNDNiMS04MzgxLWZiNzEyNzA0NDgwNQBGAAAAAACLt5QBAQ/GRYv+vEXkY5vLBwA6ksGFFTICTbjFW6e9FfRGAAAAAAEMAAA6ksGFFTICTbjFW6e9FfRGAAAu8FruAAA=" ChangeKey="CQAAABYAAAA6ksGFFTICTbjFW6e9FfRGAAAu9iR3"/>
              <t:ParentFolderId Id="AAMkADE2NjQyMjVlLWNhY2UtNDNiMS04MzgxLWZiNzEyNzA0NDgwNQAuAAAAAACLt5QBAQ/GRYv+vEXkY5vLAQA6ksGFFTICTbjFW6e9FfRGAAAAAAEMAAA=" ChangeKey="AQAAAA=="/>
              <t:ItemClass>IPM.Note</t:ItemClass>
              <t:Subject>From test</t:Subject>
              <t:Sensitivity>Normal</t:Sensitivity>
              <t:Body BodyType="HTML">Hello world</t:Body>
            </t:Message>
          </m:Items>
        </m:GetItemResponseMessage>
      </m:ResponseMessages>
    </m:GetItemResponse>
  </s:Body>
</s:Envelope>

命名空间确实可以搅浑水。

默认情况下,Nokogiri 将在根节点中查找命名空间声明,因此如果 xmlns:t 已在根节点中定义,t|Body 将起作用。

但是,因为它不是,所以您必须使用 collect_namespaces 告诉 Nokogiri 搜索文档并构建它找到的所有文档的散列。然后您可以将该散列传递给 searchcssat 或任何搜索方法:

require 'nokogiri'

doc = Nokogiri::XML(<<EOT)
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Body>
    <m:GetItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
      <t:Message>
        <t:Body BodyType="HTML">Hello world</t:Body>
      </t:Message>
    </m:GetItemResponse>
  </s:Body>
</s:Envelope>
EOT
ns = doc.collect_namespaces # => {"xmlns:s"=>"http://schemas.xmlsoap.org/soap/envelope/", "xmlns:t"=>"http://schemas.microsoft.com/exchange/services/2006/types", "xmlns:m"=>"http://schemas.microsoft.com/exchange/services/2006/messages"}
doc.at("t|Body", ns)['BodyType'] # => "HTML"

如果您阅读 collect_namespaces 的文档,您会发现存在一个潜在问题,即返回的键可能会覆盖之前找到的声明。如果存在这样的问题,您可以通过找到 s:Body 节点,然后找到它的第一个子元素,然后收集命名空间来解决这个问题:

ns = doc.at('s|Body').first_element_child.namespaces 
# => {"xmlns:m"=>"http://schemas.microsoft.com/exchange/services/2006/messages", "xmlns:t"=>"http://schemas.microsoft.com/exchange/services/2006/types", "xmlns:s"=>"http://schemas.xmlsoap.org/soap/envelope/"}

这将导致仅散列 s:Body:

中的命名空间