I am struggling in adding an attribute to a SoapVar-Element.
This is the structure i want to creat:
<SOAP-ENV:Body>
<ns:ServiceRequest>
<ns:Method>PING</ns:Method>
<ns:Property Name="name1">foo</ns:Property>
<ns:Property Name="name2">bar</ns:Property>
</ns:ServiceRequest>
</SOAP-ENV:Body>
but this is all i get:
<SOAP-ENV:Body>
<ns1:ServiceRequest>
<ns1:Method>PING</ns1:Method>
<ns1:Property>foo</ns1:Property>
<ns1:Property>bar</ns1:Property>
</ns1:ServiceRequest>
</SOAP-ENV:Body>
This is my code to create one of the "Property"-nodes:
$node = new SoapVar( 'foo', XSD_STRING, null, null, 'Property', $namespace);
I already tried replacing the 'foo' with an array or an object but nothing worked.
$Property['_'] = 'foo';
$Property['Name'] = 'name1';
$node = new SoapVar( $Property, SOAP_ENC_OBJECT);
... which results in:
<_>foo</_>
<Name>name1</Name>
Let me know if you need more information
Related
I need to generate the following XML
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://services.bloomberg.com/datalicense/dlws/ps/20071001">
<SOAP-ENV:Body>
<ns1:submitGetHistoryRequest>
<ns1:headers>
<ns1:daterange>
<ns1:period>
<ns1:start>2018-05-08</ns1:start>
<ns1:end>2018-05-08</ns1:end>
</ns1:period>
</ns1:daterange>
</ns1:headers>
<ns1:fields>
<ns1:field>PX_LAST</ns1:field>
</ns1:fields>
<ns1:instruments>
<ns1:instrument>
<ns1:id>US0000000002</ns1:id>
<ns1:yellowkey>Equity</ns1:yellowkey>
<ns1:type>ISIN</ns1:type>
</ns1:instrument>
<ns1:instrument>
<ns1:id>US0000000001</ns1:id>
<ns1:yellowkey>Equity</ns1:yellowkey>
<ns1:type>ISIN</ns1:type>
</ns1:instrument>
</ns1:instruments>
</ns1:submitGetHistoryRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I use PHP 7.1.17 and SoapClient.
I can not pass options to SoapClient, as the instrument key is repeated and PHP's associated array can not have same key twice. I tried constructing object and setting instrument properties as SoapVar, but it generates incorrect XML. Here is code and result:
$options = new \stdClass();
$options->headers = new \stdClass();
$options->headers->daterange = new \stdClass();
$options->headers->daterange->period = new \stdClass();
$options->headers->daterange->period->start = '2018-05-08';
$options->headers->daterange->period->end = '2018-05-08';
$options->fields = new \stdClass();
$options->fields->field = 'PX_LAST';
//first instrument
$instrument = new \stdClass();
$instrument->id = 'US0000000002';
$instrument->type = 'ISIN';
$instrument->yellowkey = 'Equity';
$options->instruments[] = new \SoapVar(
$instrument,
SOAP_ENC_OBJECT,
'stdClass',
"http://soapinterop.org/xsd",
"instrument"
);
//second instrument
$instrument = new \stdClass();
$instrument->id = 'US0000000001';
$instrument->type = 'ISIN';
$instrument->yellowkey = 'Equity';
$options->instruments[] = new \SoapVar(
$instrument,
SOAP_ENC_OBJECT,
'stdClass',
"http://soapinterop.org/xsd",
"instrument"
);
<ns1:instruments/> remains empty in resulting XML:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://services.bloomberg.com/datalicense/dlws/ps/20071001">
<SOAP-ENV:Body>
<ns1:submitGetHistoryRequest>
<ns1:headers>
<ns1:daterange>
<ns1:period>
<ns1:start>2018-05-08</ns1:start>
<ns1:end>2018-05-08</ns1:end>
</ns1:period>
</ns1:daterange>
</ns1:headers>
<ns1:fields>
<ns1:field>PX_LAST</ns1:field>
</ns1:fields>
<ns1:instruments/>
</ns1:submitGetHistoryRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
How can I pass options to SoapClient, to generate XML with repeated instrument keys?
I solved this. Turns out when you pass an ordered array (not associative one) as object's property, as many XML nodes are generated using that property's name, as number of elements in the array. So instead of building SoapVar objects and putting them into instruments[] array, I just assigned array of instruments to instruments as instrument property:
$options->instruments = new \stdClass();
//first instrument
$instrument = new \stdClass();
$instrument->id = 'US0000000002';
$instrument->type = 'ISIN';
$instrument->yellowkey = 'Equity';
$options->instruments->instrument[] = $instrument;
//second instrument
$instrument = new \stdClass();
$instrument->id = 'US0000000001';
$instrument->type = 'ISIN';
$instrument->yellowkey = 'Equity';
$options->instruments->instrument[] = $instrument;
I build instruments in a foreach loop. Btw converting array to object instead of building stdClass also works fine:
$options->instruments->instrument[] = (object)[
'id' => 'US0000000001',
'type' => 'ISIN',
'yellowkey' => 'Equity'
]
I'm New to webservices i'm trying to call webservice with soapClient() and it's generating Request XML which is not in expected format
Below is expected format of request XML
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body>
<ns2:EndUserRequest xmlns:ns2="http://www.example.net/EndUserRequest">
<ns2:companyCode>MD</ns2:companyCode>
<ns2:customerBranch>60</ns2:customerBranch>
<ns2:customerNumber>112946</ns2:customerNumber>
<ns2:endUserName>Some Name</ns2:endUserName>
<ns2:ContactName />
<ns2:address />
<ns2:city />
<ns2:state />
<ns2:postalCode />
<ns2:email />
<ns2:phoneNumber />
<ns2:countryCode>US</ns2:countryCode>
</ns2:EndUserRequest>
</soapenv:Body>
</soapenv:Envelope>
Below is XML Request generating by my code
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.emaple.com/EndUserRequest">
<SOAP-ENV:Body>
<ns1:EndUserRequest xmlns:ns1="http://www.example.net/EndUserRequest">
<companyCode>MD</companyCode>
<customerBranchNumber>360</customerBranchNumber>
<customerNumber>53494711</customerNumber>
<endUserName>ABCED</endUserName>
<ContactName></ContactName>
<address></address>
<city></city>
<state></state>
<postalCode></postalCode>
<email></email>
<phoneNumber></phoneNumber>
<countryCode>US</countryCode>
</ns1:EndUserRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
Below is my code
$client = new SoapClient('https://api-beta.example.com:443/enduser/v1/enduserlist?wsdl', array(
"trace" => 1,
"stream_context" => stream_context_create($streamContext),
'cache_wsdl' => WSDL_CACHE_NONE
));
$endUserRequest = new stdClass;
$endUserRequest->companyCode = 'MD';
$endUserRequest->customerBranchNumber = '560';
$endUserRequest->customerNumber = '59471321';
$endUserRequest->endUserName = 'Somename';
$endUserRequest->ContactName = '';
$endUserRequest->address = '';
$endUserRequest->city = '';
$endUserRequest->state = '';
$endUserRequest->postalCode = '';
$endUserRequest->email = '';
$endUserRequest->phoneNumber = '';
$endUserRequest->countryCode = 'US';
$requestSoapVar = new SoapVar($endUserRequest, SOAP_ENC_OBJECT,null,null,'EndUserRequest','http://www.example.com/EndUserRequest');
$res = $client->GetEndUsers($requestSoapVar);
echo '<textarea style="width:600px;height:500px">';
echo "\n-------Request Header------\n";
echo $client->__getLastRequestHeaders();
echo "\n-------Request------\n";
echo $client->__getLastRequest();
echo "\n-------Response Header------\n";
echo $client->__getLastResponseHeaders();
echo "\n-------Response------\n";
echo $client->__getLastResponse();
echo '</textarea>';
echo '<textarea style="width:600px;height:500px">';
print_r($res);
echo '</textarea>';
Every member of your object must be a SoapVar object because there is a namespace for them. Just encode your object as the follwing example shows.
$oEndUserRequest = new StdClass();
$oEndUserRequest->companyCode = new SoapVar(
'MD',
XSD_STRING,
null,
null,
'companyCode',
'http://www.example.com/EndUserRequest'
);
Just do it for everey class member and you 'll get the expected result.
For advanced reason here 's an example how to change the prefix of the namespace. You have to know, that the PHP SoapClient object nor the SoapVar object have a way to manually set a namespace prefix. In a normal case it is unnecessary to set a prefix for a namespace.
The PHP SoapClient object has a __doRequest method, in which you can edit the XML. You have to Code your own SoapClient extending the PHP SoapClient.
class MySoapClient extends SoapClient {
public function __doRequest($sRequest, $sLocation, $sAction, $iVersion, $iOneWay = 0) {
$sRequest = str_replace('ns1', 'ns2', $sRequest);
$this->__last_request = $sRequest;
return parent::__doRequest(ltrim($sRequest), $sLocation, $sAction, $iVersion, $iOneWay);
}
}
In my eyes it is not neccessary to change the namepace prefix. If so, just use the __doRequest method for your purpose.
I wanna build a request with php soap client.
The provider sent us a sample request.
here is my code;
$client = new SoapClient('http://myurl.com/TrevooWS.svc?wsdl' , array('trace' => true, 'exception' => false));
$auth_ns = 'http://schemas.datacontract.org/2004/07/Trevoo.WS.Entities.Authentication.IO';
$base_ns = 'http://schemas.datacontract.org/2004/07/Trevoo.WS.Entities.Base';
$object = new stdClass();
$form = new stdClass();
$form->Username = new SoapVar( 'USER' , XSD_STRING , null , null, 'trev1', $auth_ns);
$form->Password = new SoapVar( 'PASS' , XSD_STRING , null , null, 'trev1', $auth_ns);
$form->IsTestMode = new SoapVar( 1 , XSD_INTEGER, null , null, 'trev1', $auth_ns);
$form->ClientName = new SoapVar( 'CLIENT' , XSD_STRING , null , null, 'trev1', $auth_ns);
$form->ClientIP = new SoapVar( '0' , XSD_STRING , null , null, 'trev1', $auth_ns);
$object->request = new stdClass();
$object->request->Form = new SoapVar($form , SOAP_ENC_OBJECT, null , null, 'tem', $base_ns);
$object->request->Form->enc_namens = $auth_ns;
$object->request->Form->enc_name = 'trev1';
$object->request->enc_namens = $base_ns;
$object->request->enc_name = 'tem';
try{
$result = $client->Login( $object );
var_dump($result);
} catch (SoapFault $e){
echo $e;
var_dump($client->__getLastRequest());
var_dump($client->__getLastResponse());
}
Expected request from provider. They sent this sample request.
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:tem="http://tempuri.org/" xmlns:trev="http://schemas.datacontract.org/2004/07/Trevoo.WS.Entities.Base" xmlns:trev1="http://schemas.datacontract.org/2004/07/Trevoo.WS.Entities.Authentication.IO">
<soapenv:Header />
<soapenv:Body>
<tem:Login>
<tem:request>
<trev1:Form>
<trev1:ClientIP>0</trev1:ClientIP>
<trev1:ClientName>s</trev1:ClientName>
<trev1:IsTestMode>0</trev1:IsTestMode>
<trev1:Password>PASSWORD</trev1:Password>
<trev1:Username>USERNAME</trev1:Username>
</trev1:Form>
</tem:request>
</tem:Login>
</soapenv:Body>
</soapenv:Envelope>
What I get;
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.datacontract.org/2004/07/Trevoo.WS.Entities.Authentication.IO" xmlns:ns2="http://tempuri.org/">
<SOAP-ENV:Body>
<ns2:Login>
<ns2:request>
<ns1:Form>
<ns1:Username>USERNAME</ns1:Username>
<ns1:Password>PASSWORD</ns1:Password>
<ns1:IsTestMode>1</ns1:IsTestMode>
<ns1:ClientName>CLIENT</ns1:ClientName>
<ns1:ClientIP>0</ns1:ClientIP>
</ns1:Form>
</ns2:request>
</ns2:Login>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
This is response for Login method.
<?xml version="1.0" encoding="UTF-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Body>
<s:Fault>
<faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:InternalServiceFault</faultcode>
<faultstring xml:lang="tr-TR">String reference not set to an instance of a String.
Parameter name: s</faultstring>
<detail>
<ExceptionDetail xmlns="http://schemas.datacontract.org/2004/07/System.ServiceModel" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
<HelpLink i:nil="true" />
<InnerException i:nil="true" />
<Message>String reference not set to an instance of a String.
Parameter name: s</Message>
<StackTrace>at System.Text.Encoding.GetBytes(String s)
at Trevoo.Utilities.Crypto.MD5Hash(String input) in c:\Trevoo_TFS\*****\trunk\main\Trevoo\Projects\Core\Trevoo.Core\Utilities\Crypto.cs:line 131
at Trevoo.Accounts.AuthController.ReadonlyLogin(LoginRequest request) in c:\Trevoo_TFS\*****\trunk\main\Trevoo\Projects\Core\Trevoo.Core\Accounts\AuthController.cs:line 22
at Trevoo.Accounts.AuthController.Login(LoginRequest request) in c:\Trevoo_TFS\*****\trunk\main\Trevoo\Projects\Core\Trevoo.Core\Accounts\AuthController.cs:line 10
at Trevoo.WS.Services.Base.Login(T_LoginRequest request) in c:\Trevoo_TFS\*****\trunk\main\Trevoo\Projects\WS\Trevoo.WS\Services\Authentication\Login.cs:line 13
at SyncInvokeLogin(Object , Object[] , Object[] )
at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
at System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage5(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.ImmutableDispatchRuntime.ProcessMessage11(MessageRpc& rpc)
at System.ServiceModel.Dispatcher.MessageRpc.Process(Boolean isOperationContextSet)</StackTrace>
<Type>System.ArgumentNullException</Type>
</ExceptionDetail>
</detail>
</s:Fault>
</s:Body>
</s:Envelope>
I couldnt see what is wrong. Could you help me?
you need to order elements as expected
Ordering must be;
->ClientIP
->ClientName
->IsTestMode
->Password
->Username
Consider this simple snippet of code:
<?php
# http://php.net/manual/en/soapvar.soapvar.php
$parm = array();
$parm[] = new SoapVar('123', XSD_STRING, null, null, 'customerNo' );
$parm[] = new SoapVar('THIS', XSD_STRING, null, null, 'selection' );
$parm[] = new SoapVar('THAT', XSD_STRING, null, null, 'selection' );
$out = new SoapVar($parm, SOAP_ENC_OBJECT);
var_dump($out);
?>
On the thread it is taken from, it is claimed that it would produce something like the following XML:
<customerNo>123</customerNo>
<selection>THIS</selection>
<selection>THAT</selection>
... however, only thing I can see with var_dump() is something like this:
object(SoapVar)#4 (2) {
["enc_type"]=>
int(301)
["enc_value"]=>
array(3) {
[0]=>
object(SoapVar)#1 (3) {
["enc_type"]=>
int(101)
["enc_value"]=>
string(3) "123"
["enc_name"]=>
string(10) "customerNo"
}
[1]=> ...
How could I get the expected XML of a SoapVar object, without calling an actual remote webservice?
Ok, thanks to Inspect XML created by PHP SoapClient call before/without sending the request, I think I got it solved - one needs to create a separate debug SoapClient extended class, here is the modified OP code with it:
<?php
#<!-- # http://php.net/manual/en/class.soapvar.php -->
class SoapClientDebug extends SoapClient
{
public function __doRequest($request, $location, $action, $version, $one_way = 0) {
// Add code to inspect/dissect/debug/adjust the XML given in $request here
//echo "$request\n"; // OK, but XML string in single line
$doc = new DomDocument('1.0');
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$doc->loadXML($request);
$xml_string = $doc->saveXML();
echo "$xml_string\n";
// Uncomment the following line, if you actually want to do the request
// return parent::__doRequest($request, $location, $action, $version, $one_way);
return ""; # avoids the PHP Fatal error: Uncaught SoapFault exception: [Client] SoapClient::__doRequest() returned non string value in .../__thisfile__.php:32
}
}
# http://php.net/manual/en/soapvar.soapvar.php
$parm = array();
$parm[] = new SoapVar('123', XSD_STRING, null, null, 'customerNo' );
$parm[] = new SoapVar('THIS', XSD_STRING, null, null, 'selection' );
$parm[] = new SoapVar('THAT', XSD_STRING, null, null, 'selection' );
$out = new SoapVar($parm, SOAP_ENC_OBJECT);
var_dump($out);
//~ $dbgclient = new SoapClientDebug(NULL); # "URI of the WSDL file or NULL if working in non-WSDL mode." http://php.net/manual/en/soapclient.soapclient.php
$dbgclient = new SoapClientDebug(null, array('location' => "http://localhost/soap.php",
'uri' => "http://test-uri/"));
$dbgclient->testVar($out);
?>
This, at end, will print out:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test-uri/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body>
<ns1:testVar>
<param0 xsi:type="SOAP-ENC:Struct">
<customerNo xsi:type="xsd:string">123</customerNo>
<selection xsi:type="xsd:string">THIS</selection>
<selection xsi:type="xsd:string">THAT</selection>
</param0>
</ns1:testVar>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
... which is what I wanted, I guess...
So basically I want the SOAP header to be like this:
<soapenv:Header>
<v1:loginDetails>
<v11:Id>0</v11:Id>
<v11:username>MEMBERS</v11:username>
$ <v11:password>0x909711E5,0xE301F82A,0x0E2783CC,0xAF6BC3DB,0x57727CFB</v11:password>
</v1:loginDetails>
</soapenv:Header>
<soapenv:Body>
<v11:GetNextAvailableMemberNumberRequest>
<v11:Id>1</v11:Id>
<v11:memberId>1</v11:memberId>
</v11:GetNextAvailableNumberRequest>
</soapenv:Body>
</soapenv:Envelope>
But instead, now I have this:
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www..com/membership/types/v1_0">
<SOAP-ENV:Header>
<ns1:loginDetails>
<item><key>siteId</key><value>0</value></item>
<item><key>Username</key><value>MEMBERSHIP</value></item>
<item><key>Password</key><value>P#ssw0rd</value></item>
</ns1:loginDetails>
</SOAP-ENV:Header>
<SOAP-ENV:Body>
<ns1:GetNextAvailableMemberNumberRequest/>
<param1>1</param1>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
And this is the php code that I'm currently using:
$client = new SOAPClient('http://192.168.180.128:8010//membershipService?wsdl',array('trace' => true));
$client->__setSoapHeaders(null);
$headerbody = array ('siteId' => '0','Username' => 'MEMBERSHIP',
'Password' => 'P#ssw0rd');
$header = new SOAPHeader('http://www.com/club/services/membership/types/v1_0','loginDetails',$headerbody);
$client->__setSoapHeaders($header);
Where have I gone wrong? I seems unable to construct the header properly.
Just try to use an anonymous class instead of an array :
// Use standard class and set magic properties.
$header = new stdClass();
$header->siteId = '0';
$header->Username = 'MEMBERSHIP';
$header->Password = 'P#ssw0rd';
Casting the array of parameters to an object works pretty well
$auth = (object)array(
'Username' => 'AzureDiamond',
'Password' => 'hunter2',
);
$header = new SOAPHeader('http://my.ns/','loginDetails',$auth);
However, I find that it still doesn't get namespaces quite right. The above produces
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://my.ns/">
<SOAP-ENV:Header>
<ns2:loginDetails>
<Username>AzureDiamond</Username>
<Password>hunter2</Password>
</ns2:loginDetails>
</SOAP-ENV:Header>
....
And you'll see that although the 'loginDetails' tag is in the requested namespace, it's child elements are not.
I just encountered a very similar problem.
To fix it, you need to use the SoapVar class. This allows you to control what namespace to use for each tag.
$client = new SOAPClient('http://192.168.180.128:8010//membershipService?wsdl',array('trace' => true));
$ns = 'http://www.com/club/services/membership/types/v1_0';
$headerbody = new SoapVar([
new SoapVar('0', XSD_STRING, null, null, 'siteId', $ns),
new SoapVar('MEMBERSHIP', XSD_STRING, null, null, 'Username', $ns),
new SoapVar('P#ssw0rd', XSD_STRING, null, null, 'Password', $ns),
], SOAP_ENC_OBJECT);
$header = new SOAPHeader($ns,'loginDetails',$headerbody);
$client->__setSoapHeaders($header);
Your request's soap header should look something like this with this:
<SOAP-ENV:Header>
<ns1:loginDetails>
<ns1:siteId>0</ns2:siteId>
<ns1:Username>MEMBERSHIP</ns2:Username>
<ns1:Password>P#ssw0rd</ns2:Password>
</ns1:loginDetails>
</SOAP-ENV:Header>
There was typo:
$header = NEW stdClass() ;
$headerbody = array ('Id' => '0','username' => 'MEMBERSHIP',
'password' => 'P#ssw0rd');
Please refer the link
http://php.net/manual/en/class.soapheader.php
Also make sure that you are passing correct namespace (first parameter) in SOAPHeader constructor