PHP SoapClient删除具有名称的元素

时间:2018-06-21 15:04:50

标签: php soap-client

我有一个WSDL,其中的WSDL具有需要属性的元素:

<xsd:complexType name="claim">
  <xsd:annotation>
    <xsd:documentation>Claim Element</xsd:documentation>
  </xsd:annotation>
  <xsd:sequence>
    <!-- other elements removed -->
  </xsd:sequence>
  <xsd:attribute name="claimId" type="xsd:nonNegativeInteger" use="required" />
</xsd:complexType>

就生成的xml而言,它应类似于:

<claims>
    <claim claimId="1">
        <!-- elements removed -->
    </claim>
    <!-- more claims -->
</claims>

foreach循环中,我将元素数组组合在一起,并将属性用作键的一部分:

//$claim = array of key/value pairs
$claim = [...];
$claim = new \SoapVar($claim, SOAP_ENC_OBJECT, null, null, 'claim claimId="' . ($key+1) . '"');
$claims['claim claimId="'.($key+1).'"'] = $claim;

将其传递给SoapClient时,元素将被删除:

//$client = new \SoapClient($wsdl);
$client->checkClaims($claims);

但是我得到的只是:

<claims />

如何让我的soap客户端在soap调用中正确解析claim元素?

2 个答案:

答案 0 :(得分:1)

因此,您的代码几乎没有问题。为此,您需要在WSDL模式($client = new \SoapClient($wsdl);,正在执行)中使用SoapClient。下面的下一个是错误的

$claim = [...];
$claim = new \SoapVar($claim, SOAP_ENC_OBJECT, null, null, 'claim claimId="' . ($key+1) . '"');
$claims['claim claimId="'.($key+1).'"'] = $claim;

您不使用'claim claimId="' . ($key+1) . '"'添加属性。

现在,您需要使用classmap。以下是我创建的一个示例python flask应用程序示例,用于显示WSDL

from flask import Flask

app  = Flask(__name__)

@app.route("/ICalculator",methods=['get', 'post'])
def reply():
    return "<xmldata />"

@app.route("/app.wsdl")
def send():
    return """<wsdl:definitions
  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
  xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
  xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
  xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
  xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract"
  xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl"
  xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
  xmlns:wsa10="http://www.w3.org/2005/08/addressing"
  xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" targetNamespace="http://localhost:5001"
  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
  <wsdl:types>
    <xsd:schema targetNamespace="http://localhost:5001" elementFormDefault="qualified" >
    <xsd:element name="Claim">
    <xsd:complexType>
        <xsd:sequence>
        <xsd:element minOccurs="0" name="a" type="xsd:int" />
        <xsd:element minOccurs="0" name="b" type="xsd:int" />
      </xsd:sequence>
        <xsd:attribute name="claimId" type="xsd:nonNegativeInteger" use="required" />
    </xsd:complexType>
  </xsd:element>
  <xsd:element name="AddResponse">
    <xsd:complexType>
      <xsd:sequence>
        <xsd:element minOccurs="0" name="result" type="xsd:int" />
      </xsd:sequence>
    </xsd:complexType>
  </xsd:element>
</xsd:schema>
  </wsdl:types>
  <wsdl:message name="ICalculator_Add_InputMessage">
    <wsdl:part name="parameters" element="tns:Add" />
  </wsdl:message>
  <wsdl:message name="ICalculator_Add_OutputMessage">
    <wsdl:part name="parameters" element="tns:AddResponse" />
  </wsdl:message>
  <wsdl:portType name="ICalculator">
    <wsdl:operation name="Add">
      <wsdl:input wsaw:Action="http://localhost:5001/ICalculator/Add" message="tns:ICalculator_Add_InputMessage" />
      <wsdl:output wsaw:Action="http://localhost:5001/ICalculator/AddResponse" message="tns:ICalculator_Add_OutputMessage" />
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="DefaultBinding_ICalculator" type="tns:ICalculator">
    <soap:binding transport="http://schemas.xmlsoap.org/soap/http" />
    <wsdl:operation name="Add">
      <soap:operation soapAction="http://localhost:5001/ICalculator/Add" style="document" />
      <wsdl:input>
        <soap:body use="literal" />
      </wsdl:input>
      <wsdl:output>
        <soap:body use="literal" />
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CalculatorService">
        <wsdl:port name="ICalculator" binding="tns:DefaultBinding_ICalculator">
            <soap:address location="http://localhost:5001/ICalculator" /></wsdl:port>
</wsdl:service>
</wsdl:definitions>
""".replace(r"\r", "").replace(r"\n", "")


if __name__ == "__main__":
   app.run(debug=True, host='0.0.0.0', port=5000)

然后使用

python3 wsdl.py

然后运行另一个socat来查看流量

socat -v TCP-LISTEN:5001,fork TCP:127.0.0.1:5000

接下来,我编写了一个示例PHP代码以展示classmap的工作方式

<?php
class Claim {
  public function __construct(Array $properties=array()){
      foreach($properties as $key => $value){
        $this->{$key} = $value;
      }
    }
}

$test = new Claim(array('claimId'=>10, 'a'=> 22, 'b'=> 33));

$claim=new SoapVar($test, SOAP_ENC_OBJECT);
$wsdl = "http://localhost:5001/app.wsdl";
$client = new SoapClient($wsdl, array(
    'trace'        => 1,
    'encoding'     => 'UTF-8',
    'soap_version' => SOAP_1_1,
    'classmap'     => array('Claim' => 'Claim')
));
$client->add($claim);

结果xml

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://localhost:5001" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Body><parameters claimId="10" xsi:type="ns1:Claim"><ns1:a>22</ns1:a><ns1:b>33</ns1:b></parameters></SOAP-ENV:Body></SOAP-ENV:Envelope>

Data as attributes

参考文献:

PHP soap request with an element attribute and child elements

PHP SoapVar Object Attribute?

Adding attributes to the actual function tag in PHP soapCall

How do I add additional attributes to XML Elements with the SoapClient Class in PHP

php SoapVar not setting attributes

Getting the XML as string for a SoapVar variable - without a webservice (locally)?

Getting the XML as string for a SoapVar variable - without a webservice (locally)?

http://fvue.nl/wiki/Php:_Soap:_How_to_add_attribute_to_SoapVar

https://forums.phpfreaks.com/topic/137357-solved-php-soap-client-node-attributes/

http://eosrei.net/articles/2012/01/php-soap-xml-attributes-namespaces-xmlwriter

答案 1 :(得分:0)

知道\SoapClient可以接受xml字符串后,我决定将数据数组转换为xml字符串:

/**
 * Convert an array to an xml. Recursive function.
 * @param array $array
 * @param String $rootElement OPTIONAL name of root element.
 * @param \Simple XMLElement $xml OPTIONAL
 * @return String
 */
function arrayToXml($array, $rootElement = null, $xml = null) {
    $_xml = $xml;

    if ($_xml === null) {
        $_xml = new \SimpleXMLElement($rootElement !== null ? $rootElement : '<root/>');
    }

    foreach ($array as $k => $v) {
        if (is_array($v)) { //nested array
            arrayToXml($v, $k, $_xml->addChild($k));
      } else {
            $_xml->addChild($k, $v);
      }
    }
    //Remove xml doctype and root name spaces.
    return str_replace(["<?xml version=\"1.0\"?>\n", '<root>', "</root>\n"], '', $_xml->asXML());
}

//my array of claims
$claims = [
    'claim claimId="1"' => [
        //...
    ],
    'claim claimId="2"' => [
        //...
    ],
    //...
];
$claims = arrayToXml($claims);

这将生成一个xml字符串:

<claim claimId="1"><!-- stuff --></claim claimId="1"><claim claimId="2"><!-- stuff --></claim claimId="2">

下一步是从结束标记中删除属性:

$claims = preg_replace('#</claim claimId="\d+">#', '</claim>', $claims);

最后,我将xml包装在适当的参数名称中,并将其转换为\SoapVar,以便\SoapClient可以正确解析它:

$claims = '<ns1:claims>' . $claims . '</ns1:claims>';
$claims = new \SoapVar($claims, XSD_ANYXML, 'http://www.w3.org/2001/XMLSchema-instance');

$claims传递到我的\SoapClient中并运行var_dump($client->__getLastRequest());时,我生成的xml显示为:

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.w3.org/2001/XMLSchema-instance"><SOAP-ENV:Body><ns1:claims><claim claimId="1"><!--stuff--></claim><claim claimId="2"><!--stuff--></claim></ns1:claims></SOAP-ENV:Body></SOAP-ENV:Envelope>

随心所欲。