【问题标题】:Why I get SOAP-ERROR: Encoding: the object does not have 'tax', in WSDL with nested complextype?为什么我得到 SOAP-ERROR: Encoding: the object does not have 'tax', in WSDL with nested complextype?
【发布时间】:2018-08-02 14:57:31
【问题描述】:

我必须使用 PHP 使用 SOAP Web 服务,但出现标题错误。

wsdl的(部分)结构是这样的:

<xs:element name="Obligations" type="tns:ObligationsType"/>
<xs:complexType name="ObligationsType">
<xs:sequence>

<xs:element maxOccurs="99" name="Taxes" type="tns:TaxesType"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="TaxesType">
<xs:sequence>
<xs:element name="tax">
<xs:simpleType>
<xs:restriction base="xs:int">
<xs:maxInclusive value="9999"/>
<xs:minInclusive value="1"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="amount">
<xs:simpleType>
<xs:restriction base="xs:double">
<xs:minInclusive value="0.01"/>
<xs:maxInclusive value="9999999999.99"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:complexType>

我发送的关联数组:

    $params = array(
            'token' => $TOKEN,
            'sign' => $SIGN,
            'paymentEntity' => 1001,
            'form' => array(
                'formNumber' => 6042,
                'idPaymentType' => 951,
                'Obligations' => array (
                                     array( 
                                         'Taxes' => array(
                                                        'tax' => 6041,
                                                        'amount' => 602.0 
                                                         )
                                           )
                                        )
                             )              
                );

我试图用类来做这件事,但我得到了同样的错误。问题出在 Obligations 对象上。

我试过这样嵌套:

'Obligations' =>array ('Taxes' =>array('tax'=> 1,'amount'=> 1.0)) 我得到 Unrecognized field Obligations

'Obligations' =>array ('tax'=> 1,'amount'=> 1.0) 我得到对象没有 'Taxes' 属性

'Obligations' =>array (array('Taxes' =>array('tax'=> 1,'amount'=> 1.0))) 我得到对象没有'tax'属性

'Obligations' =>array ('Taxes' =>array('tax'=> 1)) 我得到对象没有 'amount' 属性

'Obligations' =>array ('Taxes' =>array('amount'=> 1)) 我得到对象没有 'tax' 属性

最后一个请求 xml

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="...">
<SOAP-ENV:Body>
<ns1:createForm>
<ns1:token>********</ns1:token>
<ns1:sign>********</ns1:sign>
<ns1:paymentEntity>1001</ns1:paymentEntity>
<ns1:form>
    <ns1:formNumber>6042</ns1:formNumber>
    <ns1:idPaymentType>951</ns1:idPaymentType>
    <ns1:Obligations>
        <ns1:Taxes>
            <tax>6041</tax>
            <amount>602.0</amount>
        </ns1:Taxes>
    </ns1:Obligations>
</ns1:form>
</ns1:createForm>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>

结构

"createFormResponse createForm(createForm $parameters)" [2]=> string(74)
"struct createForm { string token; string sign; int paymentEntity; FormType form; }" 
"struct FormType { formNumber formNumber; idPaymentType idPaymentType; Obligations ObligationsType; }"
"struct ObligationsType { TaxesType Taxes; }"
"struct TaxesType { tax tax; amount amount; }" 

我有一个请求示例,它与我获得的相同。 它只有一个注释

税收。该属性为列表类型。

税额。该属性为列表类型。

但它是已经在 wsdl 中标记的东西。

如果有任何建议,我将不胜感激。问候

【问题讨论】:

    标签: php arrays soap


    【解决方案1】:

    在处理复杂类型时最好使用对象。它更清洁,更容易处理。下面的例子是一个提示如何使用 php 处理复杂类型。

    /**
     * Entity from complex type ObligationsType
     * @see xsd:complexType "ObligationsType"
     */ 
    class ObligationsType
    {
        /**
         * Is an array because the containing 'TaxtesType' can appear up to 99 times
         * @var \ArrayObject
         */
        protected $Taxes;
    
        public function getTaxes() : \ArrayObject
        {
            return $this->Taxes;
        }
    
        public function setTaxes(TaxesType $taxes) : ObligationsType
        {
            if ($this->Taxes === null) {
                $this->Taxes = new \ArrayObject();
            }
    
            $this->Taxes->append($taxes);
            return $this;
        }
    
        public function encode() :\SoapVar
        {
            $container = new \ArrayObject();
            foreach (get_obect_vars($this) as $property) {
                if ($property instanceof \ArrayObject) {
                    foreach ($property as $element) {
                        $container->append($element);
                    }
                } else {
                    $container->append($property);
                }
            }
    
            return new \SoapVar(
                $container, 
                SOAP_ENC_OBJ, 
                null, 
                null, 
                'Obligations',
                'http://www.example.com/namespace/tns'
            );
        }
    }
    
    class TaxesType
    {
        protected $tax;
    
        protected $amount;
    
        public function getTax() : int
        {
            return $this->tax;
        }
    
        public function setTax(int $tax) : TaxesType
        {
            $this->tax = $tax;
            return $this;
        }
    
        public function getAmount() : float
        {
            return $this->amount;
        }
    
        public function setAmount(float $amount) : TaxesType
        {
            $this->amount = $amount;
            return $this;
        }
    
        public function encode() : \SoapVar
        {
            return new \SoapVar(
                $this,
                SOAP_ENC_OBJ,
                null,
                null,
                'Taxes',
                'http://www.example.com/namespace/tns'
            );
        }
    }
    

    所以现在我们得到了您的 wsdl als PHP 实体类中描述的两种复杂类型。这个类唯一要做的就是保存我们的数据并对其进行编码,以便通过soap客户端类发送数据。

    这个怎么用?

    告诉你的soap客户端类,你有预期数据的类。为此,soap 客户端有classmap 选项。

    $client = new \SoapClient(
        $path_to_your_wsdl,
        [
            'class_map' => [
                'ObligationsType' => ObligationsType::class,
                'TaxesType' => TaxesType::class,
            ],
            'exceptions' => true,
            'trace' => true,
        ],
    );
    
    $tax1 = (new TaxesType())
        ->setTax(19)
        ->setAmount(49.99)
        ->encode();
    
    $tax2 = (new TaxesType())
        ->setTax(7)
        ->setAmount(29.49)
        ->encode();
    
    $obligations = (new ObligationsType())
        ->setTaxes($tax1)
        ->setTaxes($tax2)
        ->encode();
    
    $result = $client->YourWebserviceFunction($obligations);
    

    这个简单的代码片段应该解释 xsd 复杂类型如何与 php soap 客户端一起工作。形象地说,每个复杂类型都是一个类,其中包含为复杂类型描述的成员。这种理解使得处理soap请求和响应中的数据变得容易。此外,每个 web 服务函数都有一个在 wsdl 或任何绑定的 xsd 文件中描述的返回类型。找到这个返回类型并从中创建一个 php 类,并在soap 客户端的classmap 选项中提及它。 soap 客户端可以轻松地在返回类型类中对请求的返回进行水合,这在 classmap 选项中有所提及。

    如您所见,SOAP 和 PHP 可以很好地协同工作。

    永远记住:上面显示的代码未经测试。请不要在生产中使用此代码。

    编辑:如何在 try/catch 块中获取最后一个请求

    正如您在此答案下的 cmets 中提到的,您无法获得最后一个请求。我将在接下来的几行中向您展示如何做到这一点。只需将肥皂客户端初始化包装在 try catch 块中。如果soap 客户端已经初始化,那么它也可以在catch 块中访问。

    try {
        $client = new \SoapClient(
            $wsdl_path,
            $options
        );
    
        // call of your webservice methods here
        ...
    
    } catch (\SoapFault $e) {
        // get the last request in the catch block
        if ($client !== null) {
            echo "<pre>";
            var_dump(htmlentities($client->__getLastRequest()));
            echo "</pre>";
        }
    }
    

    不要忘记将 trace 选项设置为 true。如果不是,你不能调用soap客户端的__getLastRequest()方法。

    请使用最后一个 xml 请求编辑您的问题。

    【讨论】:

    • 谢谢,我试过这种方法,但我仍然得到同样的错误
    • 你能发布一个你发送的最后一个请求的 XML 示例吗?当您使用跟踪选项创建soap 客户端时,您可以使用$client-&gt;__getLastRequest() 获取最后一个请求。只需将您的最后一个请求添加到上面的问题中即可。
    • 我知道命令,但无法显示请求,因为我得到 致命错误:未捕获的 SoapFault 异常:[soapenv:Server.userException] com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException : 无法识别的字段“义务”
    • 我已经编辑了我的答案并添加了一个示例,即如何获取 catch 块中的最后一个请求。请按照显示的代码并使用最后一个请求 xml 编辑您的问题。
    • 我添加了最后一个请求 xml。
    猜你喜欢
    • 1970-01-01
    • 2020-10-28
    • 2021-09-07
    • 2021-12-29
    • 2016-05-06
    • 1970-01-01
    • 1970-01-01
    • 2016-08-23
    • 1970-01-01
    相关资源
    最近更新 更多