如何解析 SOAP XML?
Posted
技术标签:
【中文标题】如何解析 SOAP XML?【英文标题】:How to parse SOAP XML? 【发布时间】:2011-05-10 19:38:22 【问题描述】:SOAP XML:
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<PaymentNotification xmlns="http://apilistener.envoyservices.com">
<payment>
<uniqueReference>ESDEUR11039872</uniqueReference>
<epacsReference>74348dc0-cbf0-df11-b725-001ec9e61285</epacsReference>
<postingDate>2010-11-15T15:19:45</postingDate>
<bankCurrency>EUR</bankCurrency>
<bankAmount>1.00</bankAmount>
<appliedCurrency>EUR</appliedCurrency>
<appliedAmount>1.00</appliedAmount>
<countryCode>ES</countryCode>
<bankInformation>Sean Wood</bankInformation>
<merchantReference>ESDEUR11039872</merchantReference>
</payment>
</PaymentNotification>
</soap:Body>
</soap:Envelope>
如何获取“支付”元素?
我尝试解析(php)
$xml = simplexml_load_string($soap_response);
$xml->registerXPathNamespace('soap', 'http://schemas.xmlsoap.org/soap/envelope/');
foreach ($xml->xpath('//payment') as $item)
print_r($item);
结果为空:( 任何想法如何正确解析它?
【问题讨论】:
欢迎来到 SO。下一次,点击文本区域顶部的格式按钮,这样代码更容易阅读。 @stillstanding:哦,显然我在您提交时覆盖了您的编辑 :) 由于您是新手,我建议您阅读***.com/faq#howtoask,然后接受我的回答,因为这是最相关的;-) 【参考方案1】:处理命名空间前缀的最简单方法之一就是在将 XML 响应传递给 simplexml 之前将其从 XML 响应中剥离,如下所示:
$your_xml_response = '<Your XML here>';
$clean_xml = str_ireplace(['SOAP-ENV:', 'SOAP:'], '', $your_xml_response);
$xml = simplexml_load_string($clean_xml);
这将返回以下内容:
SimpleXMLElement Object
(
[Body] => SimpleXMLElement Object
(
[PaymentNotification] => SimpleXMLElement Object
(
[payment] => SimpleXMLElement Object
(
[uniqueReference] => ESDEUR11039872
[epacsReference] => 74348dc0-cbf0-df11-b725-001ec9e61285
[postingDate] => 2010-11-15T15:19:45
[bankCurrency] => EUR
[bankAmount] => 1.00
[appliedCurrency] => EUR
[appliedAmount] => 1.00
[countryCode] => ES
[bankInformation] => Sean Wood
[merchantReference] => ESDEUR11039872
)
)
)
)
【讨论】:
命名空间通常具有误导性,删除它似乎是恢复正确 xml 对象的最快(黑客)方法。谢谢 很棒的解决方案。肥皂很烂。 就我而言,我不得不向 str_ireplace 添加更多数组值,但最后这是我找到的唯一可行的解决方案。赞成。也同意@JohnBallinger 肥皂很烂。 谢谢!这节省了我很多时间。只需用 str_replace 替换命名空间,一个简单的 XML 对象就可以工作。 向 str_ireplace 添加了一些值对我有用 :) ['SOAP-ENV:', 'env:', 'SOAP:', 'ns1:']【参考方案2】:PHP 版本 > 5.0 集成了一个不错的 SoapClient。不需要解析响应xml。这是一个简单的例子
$client = new SoapClient("http://path.to/wsdl?WSDL");
$res = $client->SoapFunction(array('param1'=>'value','param2'=>'value'));
echo $res->PaymentNotification->payment;
【讨论】:
SoapFunction 是什么? @Edward 它是您要访问的 SOAP 服务的 WSDL 中定义的任何功能(操作) @Ivan 那么它是 asmx 服务器上的 XML 中列出的服务之一吗?顺便说一句,你认为你可以看看我与此相关的问题吗? ***.com/questions/17042984/… @Edvard 是的。 SOAP 服务器将定义它可以执行哪些类型的操作以及每个操作需要哪些参数。所以在上面的例子中。 “Soap Function”是由 Soap 服务器定义的操作名称。参数进入数组【参考方案3】:在您的代码中,您正在查询默认命名空间中的 payment
元素,但在 XML 响应中,它被声明为 http://apilistener.envoyservices.com
命名空间中的元素。
所以,您缺少命名空间声明:
$xml->registerXPathNamespace('envoy', 'http://apilistener.envoyservices.com');
现在您可以在 xpath 查询中使用 envoy
命名空间前缀:
xpath('//envoy:payment')
完整的代码是:
$xml = simplexml_load_string($soap_response);
$xml->registerXPathNamespace('envoy', 'http://apilistener.envoyservices.com');
foreach ($xml->xpath('//envoy:payment') as $item)
print_r($item);
注意:我删除了 soap
命名空间声明,因为您似乎没有使用它(仅当您在 xpath 查询中使用命名空间前缀时才有用)。
【讨论】:
但是为什么命名空间是“envoy”?为什么不用“肥皂”? 问题中的 XML 文档包含名称空间 URI "apilistener.envoyservices.com" 的元素,问题是关于如何查询该名称空间中的元素。您不必调用前缀“envoy”(可以是 XPath 查询范围内的任何唯一名称),但“envoy”似乎是一个基于名称空间 URI 的逻辑名称。我不会称它为“soap”,因为它应该为“schemas.xmlsoap.org/soap/envelope”命名空间保留。【参考方案4】:$xml = '<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<PaymentNotification xmlns="http://apilistener.envoyservices.com">
<payment>
<uniqueReference>ESDEUR11039872</uniqueReference>
<epacsReference>74348dc0-cbf0-df11-b725-001ec9e61285</epacsReference>
<postingDate>2010-11-15T15:19:45</postingDate>
<bankCurrency>EUR</bankCurrency>
<bankAmount>1.00</bankAmount>
<appliedCurrency>EUR</appliedCurrency>
<appliedAmount>1.00</appliedAmount>
<countryCode>ES</countryCode>
<bankInformation>Sean Wood</bankInformation>
<merchantReference>ESDEUR11039872</merchantReference>
</payment>
</PaymentNotification>
</soap:Body>
</soap:Envelope>';
$doc = new DOMDocument();
$doc->loadXML($xml);
echo $doc->getElementsByTagName('postingDate')->item(0)->nodeValue;
die;
结果是:
2010-11-15T15:19:45
【讨论】:
这可行,但在技术上并不正确。您正在考虑将 SOAP XML 响应视为一个 DOM 文档,但事实并非如此。 这有效,但仅适用于传递给“$xml”变量的静态 xml。向其传递 SOAP 响应时返回空结果。【参考方案5】:首先,我们需要过滤 XML 以便将其解析为对象
$response = strtr($xml_string, ['</soap:' => '</', '<soap:' => '<']);
$output = json_decode(json_encode(simplexml_load_string($response)));
var_dump($output->Body->PaymentNotification->payment);
【讨论】:
【参考方案6】:如果您随后需要将任何对象解析为数组,这也非常好: $array = json_decode(json_encode($responseXmlObject), true);
【讨论】:
【参考方案7】:首先,我们需要过滤XML,以便将更改对象解析为数组
//catch xml
$xmlElement = file_get_contents ('php://input');
//change become array
$Data = (array)simplexml_load_string($xmlElement);
//and see
print_r($Data);
【讨论】:
【参考方案8】:你为什么不尝试使用绝对 xPath
//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment
或者既然你知道这是一种支付方式,而支付方式没有任何属性,直接从支付方式中选择
//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment/*
【讨论】:
foreach ($xml->xpath('//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment/*') as $item) print_r( $项目); 也是空的( 尝试给你的第二个命名空间一个实际值。然后定义它。 你能举个例子吗 你可以在我的回复中看到这种方法 ;-) 结果相同 -> foreach ($xml->xpath('//soap:Envelope[1]/soap:Body[1]/PaymentNotification[1]/payment/*') 为$item)print_r($item);以上是关于如何解析 SOAP XML?的主要内容,如果未能解决你的问题,请参考以下文章