【问题标题】:SoapClient returning response with empty stdClass objectsSoapClient 返回带有空 stdClass 对象的响应
【发布时间】:2014-08-08 19:56:16
【问题描述】:

我遇到了 PHP SoapClient 的问题。我有以下 WSDL https://api.mindbodyonline.com/0_5/DataService.asmx?WSDL,我正在调用 SelectDataXml。它返回适当的响应,其中包括与 SQL 查询将返回的数据行相对应的对象数组。我已经在 Soap UI 和 __getLastResponse 方法(使用 print_r 例程和 NetBeans 调试器窗口)中检查了响应。我可以将完整的响应视为一个字符串,但该数组是用空的 stdClasses 构建的。我在这里看到了几个答案,建议关闭缓存来解决这个问题。我试过了,但没有。我似乎找不到让它正确构建对象的方法。任何帮助将不胜感激。

已编辑

抽象超类(敏感数据替换为'REMOVED'):

abstract class Mindbody_service {
    protected $_userCredentials = array ( "Username" => 'REMOVED', "Password" => 'REMOVED', 'SiteIDs' => array ( 'REMOVED' ) ),
        $_sourceCredentials = array ( "SourceName" => 'REMOVED', "Password" => 'REMOVED', 'SiteIDs' => array ( 'REMOVED' ) ),
        $_endPoint;

    public function __construct( $wsdl, $options = array () )
    {
        try {
            $this->_endPoint = new SoapClient( $wsdl, $options );
        } catch ( SoapFault $fault ) {
            echo $fault->getMessage();
        }
    }
}

具体的子类:

include_once ('Mindbody_service.php');

class MindbodyDataServiceResponse {

    public $Status,
        $ErrorCode,
        $XMLDetail,
        $ResultCount,
        $CurrentPageIndex,
        $TotalPageCount,
        $Results;

    function __construct()
    {
        $Status = '';
        $ErrorCode = 0;
        $XMLDetail = '';
        $ResultCount = 0;
        $CurrentPageIndex = 0;
        $TotalPageCount = 0;
        $Results = new MindbodyDataServiceResults();
    }
}

class MindbodyDataServiceResults {

    public $Row;

    function __construct()
    {
        $row = array ();
    }
}

class Mindbody_data_service extends Mindbody_service {

    private $_query = "Very long SQL command that makes sense to the server";

    public function __construct()
    {
        $wsdl = 'https://api.mindbodyonline.com/0_5/DataService.asmx?wsdl';
        $options = array ();
        $classMap = array ( 'SelectDataXmlResult' => 'MindbodyDataServiceResponse' );

        $options [ 'trace' ] = TRUE;
        $options [ 'cache_wsdl' ] = WSDL_CACHE_NONE;
        $options [ 'compression' ] = SOAP_COMPRESSION_ACCEPT | SOAP   _COMPRESSION_GZIP;
        $options [ 'classmap' ] = $classMap;

        parent::__construct( $wsdl, $options );
    }

    public function getSomething( $since = null )
    {
        $request = array ( 'SourceCredentials' => $this->_sourceCredentials );

        $request [ 'SourceCredentials' ] = $this->_sourceCredentials;
        $request [ 'UserCredentials' ] = $this->_userCredentials;
        $request [ 'XMLDetail' ] = 'Full';
        $request [ 'PageSize' ] = 0;
        $request [ 'CurrentPageIndex' ] = 0;
        $request [ 'SelectSql' ] = $this->_conditionalQuery( $since ) . ' ORDER BY Sales.SaleDate;';

        try {
            $result = $this->_endPoint->SelectDataXml( array ( 'Request' => $request ) );
        } catch ( SoapFault $fault ) {
            echo 'ERROR: [' . $fault->faultcode . '] ' . $fault->faultstring . '.';
            exit;
        } catch ( Exception $e ) {
            echo 'ERROR: ' . $e->getMessage() . '.';
            exit;
        }

        echo '<p>';
        var_dump ($result->SelectDataXmlResult);
        echo '</p>';

        return $result->SelectDataXmlResult;
    }
}

有问题的方法是getSomething。 $since 变量无关紧要,它在查询中用作检索数据的截止日期(为此我有一个私有 _conditionalQuery( $since ) 方法)。

我在浏览器上得到的 var_dump ($result->SelectDataXmlResult) 的输出是:

object(MindbodyDataServiceResponse)[22]
  public 'Status' => string 'Success' (length=7)
  public 'ErrorCode' => int 200
  public 'XMLDetail' => string 'Full' (length=4)
  public 'ResultCount' => int 0
  public 'CurrentPageIndex' => int 0
  public 'TotalPageCount' => int 0
  public 'Results' => 
    object(stdClass)[23]
      public 'Row' => 
        array (size=4)
          0 => 
            object(stdClass)[24]
              ...
          1 => 
            object(stdClass)[25]
              ...
          2 => 
            object(stdClass)[26]
              ...
          3 => 
            object(stdClass)[27]
              ...

已编辑

这是服务器的响应:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <SelectDataXmlResponse xmlns="http://clients.mindbodyonline.com/api/0_5">
            <SelectDataXmlResult>
                <Status>Success</Status>
                <ErrorCode>200</ErrorCode>
                <XMLDetail>Bare</XMLDetail>
                <ResultCount>0</ResultCount>
                <CurrentPageIndex>0</CurrentPageIndex>
                <TotalPageCount>0</TotalPageCount>
                <Results>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                    <Row>
                        <Column1>REMOVED</Column1>...(more columns)
                    </Row>
                </Results>
            </SelectDataXmlResult>
        </SelectDataXmlResponse>
    </soap:Body>
</soap:Envelope>

我删除了每个 Row 元素的实际内容,因为它是客户数据,但它们是格式良好的 XML 元素。正是那些我作为空 stdClass 对象获得的元素,我应该在其中获得 stdClass 对象,其成员与 Row 元素的子元素具有 1:1 对应关系。

【问题讨论】:

  • 你需要一个关于如何通过soapclient调用方法的例子吗?
  • 不,我已经做得很好了。正如我上面所说的,问题在于调用返回了不应返回的空对象。
  • 你能把你的代码贴在这里吗?
  • 添加了代码,加上 var_dump 的输出 :)
  • 不可能,除了作为服务的消费者之外,我无法访问服务器。无论如何,我已经解决了这个问题。似乎 PHP 的 SoapClient 内部解析器在某些条件下(我碰巧遇到)默默地失败了。我所做的是丢弃我正在获取的对象并请求原始 XML 响应,我将其传递给 simplexml_load_string 例程以获取我可以实际使用的对象。稍后我将发布代码,因为由于 StackOverflow 的限制,我目前无法发布。

标签: php soap-client


【解决方案1】:

找到解决方案!在某些情况下,SoapClient 似乎无法解析响应。我通过重写 getSomething 方法以丢弃不正确的结果并使用 simplexml_load_string 例程生成对象来解决这个问题。我基于线程simplexml_load_string() will not read soap response with "soap:" in the tags。我之前尝试过这个,但是因为我没有考虑到 SOAP 命名空间冲突(即我单独使用 simplexml_load_string :P),所以遇到了障碍。这是工作代码:

public function getSoldProducts( $since = null )
    {
        $request = array ( 'SourceCredentials' => $this->_sourceCredentials );

        $request [ 'UserCredentials' ] = $this->_userCredentials;
        $request [ 'XMLDetail' ] = 'Full';
        $request [ 'PageSize' ] = 0;
        $request [ 'CurrentPageIndex' ] = 0;
        $request [ 'SelectSql' ] = $this->_conditionalQuery( $since ) . ' ORDER BY Sales.SaleDate;';

        try {
            $this->_endPoint->SelectDataXml( array ( 'Request' => $request ) );
            $xml = simplexml_load_string($this->_endPoint->__getLastResponse ());
            $xml->registerXPathNamespace("soap", "http://www.w3.org/2003/05/soap-envelope");
            $result = $xml->xpath('//soap:Body');
        } catch ( SoapFault $fault ) {
            echo 'ERROR: [' . $fault->faultcode . '] ' . $fault->faultstring . '.';
            exit;
        } catch ( Exception $e ) {
            echo 'ERROR: ' . $e->getMessage() . '.';
            exit;
        }

        return $result[0]->SelectDataXmlResponse->SelectDataXmlResult;
    }

希望这对遇到类似问题的人有所帮助。

【讨论】:

    【解决方案2】:

    我遇到过类似的情况,差点把头发拔掉,但最终我把它整理好了。

    在我无法更改任何内容的 Soap 服务器上,SoapUI 和 Java 能够获得响应,但 PHP \SoapClient->__soapCall(...) 的默认行为是将一些元素作为空的 StdClass 对象返回。

    我找到的解决方案并不理想,但可以完成。

    基本上,我想要原始响应,所以我可以自己解析它,并且 \SoapClient->__getLastResponse() 会做到这一点,当且仅当我们将选项键 'trace' 设置为 1。

    所以,伪代码:

    $options = [
       // (login, password, etc...)
       'trace' => 1, 
    ];
    $client = new \SoapClient($wsdl, $options);
    
    // (prepare the data for the call...)
    
    $result = $client->__soapCall('save', [$callContext, $name, $inputXml]);
    
    // Because of trace = 1 in options, this will now work.
    $rawResult = $soapClient->__getLastResponse();
    

    现在您可以完全访问响应,只需解析它。

    希望这对某人有所帮助!

    【讨论】:

      猜你喜欢
      • 2011-11-17
      • 2022-01-01
      • 2013-03-10
      • 1970-01-01
      • 2019-01-25
      • 1970-01-01
      • 2018-09-09
      • 2018-02-12
      • 2014-10-29
      相关资源
      最近更新 更多