将复杂SOAP XML请求转为PHP数组传入nusoap_client遇错求助
解决PHP调用Java SOAP服务的SAAJ对象模型创建错误
嘿,我之前也碰到过类似的问题,这个soap:Client Problems creating SAAJ object model错误,十有八九是因为你通过XML转数组生成的SOAP请求,和Java端(大概率用JAXB这类框架解析)期望的结构不匹配——XML的命名空间、嵌套层级、元素顺序甚至Header信息,在Java的SOAP解析逻辑里都是严格要求的,直接用simplexml_load_string+json_decode转数组很容易丢失这些关键细节。
下面给你几个针对性的解决方案和排查步骤:
1. 最稳妥的办法:直接复用SoapUI的正确XML请求
既然SoapUI的请求能正常工作,那完全可以跳过数组转换这一步,用nusoap的send方法直接发送原始SOAP XML:
$xml = file_get_contents('test.xml'); require_once('nusoap.php'); // 第二个参数传false,用非WSDL模式直接发送原始XML $client = new nusoap_client("http://localhost/service", false); // 发送原始SOAP请求 $response = $client->send($xml); // 处理响应 if ($client->fault) { print_r($response); } else { $err = $client->getError(); if ($err) { echo "Request Error: " . $err; } else { print_r($response); } }
这种方式1:1复刻了SoapUI的成功请求,完全避免了数组转XML时的结构偏差问题。
2. 若坚持用数组传参,先修正几个明显错误
如果你一定要用call方法传数组,先改这几个致命问题:
- 方法名错误:你代码里
call('Input', ...)是错的!Java端的方法是inputDetails(Input input),所以第一个参数应该是inputDetails,不是Input。 - 缺失Header信息:你的XML里有
soapenv:Header下的v3:Info,但数组里完全没包含这部分,Java端可能依赖这个Header做校验。 - 命名空间要完全匹配:
$mynamespace必须和XML里Input元素的命名空间http://../conf/ident/ial/../input/完全一致,不能随便写。
修正后的数组结构大概是这样(要严格对应XML的嵌套层级):
// 构造Header数组 $headerArray = [ 'Info' => [ 'requestId' => 'Sample' ] ]; // 给客户端设置Header,注意命名空间是v3的那个 $client->addHeader('http:///services/common/commondf/v3/', 'Info', $headerArray); // 构造请求参数数组,对应Input对象 $requestArray = [ 'Input' => [ 'a' => [ 'b' => [ 'scheme' => '123', 'value' => 'aaa' ] ], 'c' => [ 'date' => '2018-04-09', 'e' => [ 'scheme' => '1234', 'value' => 'bbb' ] ], // 按XML结构补充剩下的字段 ] ]; // 调用正确的方法名inputDetails $response = $client->call('inputDetails', $requestArray, $mynamespace);
3. 关键排查步骤:对比生成的请求XML和SoapUI的正确请求
不管用哪种方式,都要查看nusoap生成的实际请求XML,和SoapUI的正确请求对比,找出差异:
// 在调用send或call之后,打印请求XML echo "<pre>生成的请求XML:\n" . htmlspecialchars($client->request) . "</pre>";
重点对比这几个部分:
- 所有命名空间(Envelope、Header、Body、Input的命名空间)是否完全一致
- 元素的嵌套层级和顺序是否和SoapUI的XML一致(Java的SOAP解析框架很多依赖元素顺序)
- Header部分是否存在且结构正确
4. 考虑替换成PHP原生SoapClient
nusoap已经很久没维护了,PHP原生的SoapClient对SOAP的支持更完善,尤其是处理复杂的对象结构和命名空间时,会自动根据WSDL生成正确的XML:
$wsdl = 'http://localhost/service?wsdl'; $client = new SoapClient($wsdl, [ 'trace' => true, // 开启调试,方便查看请求响应 'exceptions' => true ]); // 构造Header $infoHeader = new stdClass(); $infoHeader->requestId = 'Sample'; $soapHeader = new SoapHeader( 'http:///services/common/commondf/v3/', 'Info', $infoHeader ); $client->__setSoapHeaders($soapHeader); // 构造Input对象,严格对应Java的Input类结构 $input = new stdClass(); $input->a = new stdClass(); $input->a->b = new stdClass(); $input->a->b->scheme = '123'; $input->a->b->value = 'aaa'; $input->c = new stdClass(); $input->c->date = '2018-04-09'; $input->c->e = new stdClass(); $input->c->e->scheme = '1234'; $input->c->e->value = 'bbb'; // 补充其他字段 try { $response = $client->inputDetails($input); print_r($response); } catch (SoapFault $e) { echo "Error: " . $e->getMessage() . "\n"; // 打印请求XML对比 echo "<pre>请求XML:\n" . htmlspecialchars($client->__getLastRequest()) . "</pre>"; }
原生SoapClient会帮你处理大部分命名空间和结构问题,出错概率低很多。
内容的提问来源于stack exchange,提问作者Harsha
相关产品推荐
相关产品推荐

