2

In my SOAP call I need to have multiple elements with the same name.

I found a way how to do this in this post: http://andrecatita.com/code-snippets/php-soap-repeated-element-name/

Hoewever, for some reason I'm getting BOGUS tag instead of the needed TranItem.

The php code is:

$itemParams = new ArrayObject();
$items = new ArrayObject();
$tranItems = new stdClass();

$itemParams[0]->ItemId = '1234';
$itemParams[0]->Amount = '1';
$itemParams[0]->Price = '2000';
$itemParams[0]->TranRules = array('TranRule' => $itemRule);

$itemParams[1]->ItemId = '55555';
$itemParams[1]->Amount = '1';
$itemParams[1]->Price = '2000';
$itemParams[1]->TranRules = array('TranRule' => $itemRule);

$items->append($itemParams[0]);
$items->append($itemParams[1]);

$tranItems->TranItems = new SoapVar($items, SOAP_ENC_OBJECT, NULL, NULL, 'TranItems',$namespace);
...
...

 $trans = array(
   'TranItems' => $tranItems->TranItems
);

which outputs:

         <ns1:TranItems>
            <BOGUS>
              <ItemId>1234</ItemId>
              <Amount>1</Amount>
              <Price>2000</Price>
              <TranRules>
                <item>
                  <key>TranRule</key>
                  <value/>
                </item>
              </TranRules>
            </BOGUS>
            <BOGUS>
              <ItemId>55555</ItemId>
              <Amount>1</Amount>
              <Price>2000</Price>
              <TranRules>
                <item>
                  <key>TranRule</key>
                  <value/>
                </item>
              </TranRules>
            </BOGUS>
          </ns1:TranItems>

How can I get rid of the BOGUS element and have TranItem in there instead?

user1049961
  • 2,656
  • 9
  • 37
  • 69
  • Have a look at this [post](http://stackoverflow.com/questions/4855677/soapvar-param-and-nested-repeated-elements-in-soap) and @Mark F's answer – JamesG Feb 12 '14 at 20:06
  • I saw that one already, didn't find out how to fix my code though... – user1049961 Feb 12 '14 at 20:18

2 Answers2

2

I ended up with this working code - might not be the best solution, but this is the only one that worked for me:

class Item {
    public $ItemId;
    public $Amount;
    public $Price;
    public $TranRules;
}

class TranRule {
    public $RuleId;
    public $Value;
}

$rule1 = new TranRule();
$rule1->RuleId = '2';
$rule1->Value = '300';
$rule1 = new SoapVar($rule1, SOAP_ENC_OBJECT, NULL, NULL, 'TranRule', 'http://gate.ibod.cz/v1/api.svc');

$return_array = new ArrayObject();

$new_item  = new Item();
$new_item->ItemId = '13245';
$new_item->Amount = "1";
$new_item->Price = "500";
$new_item->TranRules = $rule1;
$new_item = new SoapVar($new_item, SOAP_ENC_OBJECT, null, null, 'TranItem');
$return_array->append($new_item);

$new_item  = new Item();
$new_item->ItemId = '456789';
$new_item->Amount = "1";
$new_item->Price = "20000";
$new_item->TranRules = $rule2;
$new_item = new SoapVar($new_item, SOAP_ENC_OBJECT, null, null, 'TranItem');
$return_array->append($new_item);

$tranItems = new stdClass();
$tranItems->TranItems = new SoapVar($return_array, SOAP_ENC_OBJECT, NULL, NULL, 'TranItems');
user1049961
  • 2,656
  • 9
  • 37
  • 69
  • That is also how I ended up solving a similar problem. You seem to have to be explicit in creating arrays of SoapVar and specify the tag name to be used in the SOAP response. – Halfstop May 18 '16 at 16:30
  • That `SoapVar` uglifies whole idea of RPC, why SoapServer can't get that name from wsdl schema? – vp_arth Feb 19 '21 at 10:09
1

From this server:

$server = new SoapServer(null, array(
    'uri'       => 'http://test/',
));

$server->addFunction('test');

function test(){
    $itemParams = new ArrayObject();
    $items = array();

    $itemParams[0]->ItemId = '1234';
    $itemParams[0]->Amount = '1';
    $itemParams[0]->Price = '2000';
    $itemParams[0]->TranRules = array('TranRule' => 1);

    $itemParams[1]->ItemId = '55555';
    $itemParams[1]->Amount = '1';
    $itemParams[1]->Price = '2000';
    $itemParams[1]->TranRules = array('TranRule' => 2);

    $items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT);
    $items[] = new SoapVar($itemParams[1], SOAP_ENC_OBJECT);

    return new SoapVar($items, SOAP_ENC_OBJECT, NULL, NULL, 'TranItems');
}

$server->handle();

With this client:

$client = new SoapClient(null, array(
    'location' => 'http://localhost/server.php',
    'uri'       => 'http://test/',
    'trace'     => true
));

$p = $client->test();

$resp = $client->__getLastResponse();
$dom = new DOMDocument();
$dom->formatOutput = true;
$dom->preserveWhiteSpace = false;
$dom->loadXML($resp);
print '<pre>';
var_dump($p);
print htmlspecialchars($dom->saveXML());

I'm getting this response:

object(stdClass)[2]
  public 'TranItems' => 
    array (size=2)
      0 => 
        object(stdClass)[3]
          public 'ItemId' => string '1234' (length=4)
          public 'Amount' => string '1' (length=1)
          public 'Price' => string '2000' (length=4)
          public 'TranRules' => 
            array (size=1)
              ...
      1 => 
        object(stdClass)[4]
          public 'ItemId' => string '55555' (length=5)
          public 'Amount' => string '1' (length=1)
          public 'Price' => string '2000' (length=4)
          public 'TranRules' => 
            array (size=1)
              ...
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://xml.apache.org/xml-soap" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <ns1:testResponse>
      <ns1:TranItems xsi:type="SOAP-ENC:Struct">
        <TranItems xsi:type="SOAP-ENC:Struct">
          <ItemId xsi:type="xsd:string">1234</ItemId>
          <Amount xsi:type="xsd:string">1</Amount>
          <Price xsi:type="xsd:string">2000</Price>
          <TranRules xsi:type="ns2:Map">
            <item>
              <key xsi:type="xsd:string">TranRule</key>
              <value xsi:type="xsd:int">1</value>
            </item>
          </TranRules>
        </TranItems>
        <TranItems xsi:type="SOAP-ENC:Struct">
          <ItemId xsi:type="xsd:string">55555</ItemId>
          <Amount xsi:type="xsd:string">1</Amount>
          <Price xsi:type="xsd:string">2000</Price>
          <TranRules xsi:type="ns2:Map">
            <item>
              <key xsi:type="xsd:string">TranRule</key>
              <value xsi:type="xsd:int">2</value>
            </item>
          </TranRules>
        </TranItems>
      </ns1:TranItems>
    </ns1:testResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

With minor modification:

$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT, 'TransItem');
$items[] = new SoapVar($itemParams[1], SOAP_ENC_OBJECT, 'TransItem');

return new SoapVar($items, SOAP_ENC_ARRAY, 'TransItem', null);

You will get:

array (size=2)
  0 => 
    object(stdClass)[2]
      public 'ItemId' => string '1234' (length=4)
      public 'Amount' => string '1' (length=1)
      public 'Price' => string '2000' (length=4)
      public 'TranRules' => 
        array (size=1)
          'TranRule' => int 1
  1 => 
    object(stdClass)[3]
      public 'ItemId' => string '55555' (length=5)
      public 'Amount' => string '1' (length=1)
      public 'Price' => string '2000' (length=4)
      public 'TranRules' => 
        array (size=1)
          'TranRule' => int 2
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://test/" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:ns2="http://xml.apache.org/xml-soap" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body>
    <ns1:testResponse>
      <return SOAP-ENC:arrayType="TransItem[2]" xsi:type="TransItem">
        <item xsi:type="TransItem">
          <ItemId xsi:type="xsd:string">1234</ItemId>
          <Amount xsi:type="xsd:string">1</Amount>
          <Price xsi:type="xsd:string">2000</Price>
          <TranRules xsi:type="ns2:Map">
            <item>
              <key xsi:type="xsd:string">TranRule</key>
              <value xsi:type="xsd:int">1</value>
            </item>
          </TranRules>
        </item>
        <item xsi:type="TransItem">
          <ItemId xsi:type="xsd:string">55555</ItemId>
          <Amount xsi:type="xsd:string">1</Amount>
          <Price xsi:type="xsd:string">2000</Price>
          <TranRules xsi:type="ns2:Map">
            <item>
              <key xsi:type="xsd:string">TranRule</key>
              <value xsi:type="xsd:int">2</value>
            </item>
          </TranRules>
        </item>
      </return>
    </ns1:testResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

Explanation:

Soap was developed as a cross language transport protocol. In most of the languages there is no such thing as associative arrays. Thats why you have to encode $itemParams[0] as object. Most of the target languages for soap (like Java) have strong types so when you don't provide type for soap encoder it will generate one. Same goes for an array. In strongly typed language, every array can hold only objects of one kind. To sum it up: you encode php associative array as object of TYPE where TYPE will become node name and then if you return multiple elements: you encode them as soap array holding objects of specific TYPE. Not encoded array (because it can contain multiple different types) is converted into a map with multiple item nodes, each with key node and a value node.

Change this:

$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT, null, null, 'TranItems');

to that:

$items[] = new SoapVar($itemParams[0], SOAP_ENC_OBJECT);

And you will get BOGUS back.

Greg
  • 698
  • 4
  • 11
  • Thanks for detailed answer, this didn't work for me though, as it adds another `` tag inside the TranRules – user1049961 Feb 12 '14 at 21:53
  • Well you asked "How can I get rid of the BOGUS element and have TranItem in there instead?" so it is done :) For TranRules just use the same approach: `$itemParams[0]->TranRules = new SoapVar(array(1,2,3), SOAP_ENC_ARRAY, NULL, NULL, 'TranRule');` – Greg Feb 12 '14 at 22:11