How to pass a Dictionary<Key, Value> from C# to PHP

I believe that the simplest way to pass complex data from C# to PHP is through WCF service. See my previous post How to implement WCF service in PHP for more details.

Let assume we have a WCF service contract that has the following method with some nested dictionary as a parameter:

[ServiceContract]
public interface IStore
{
    [OperationContract]
    void UpdateRows(Dictionary<int, Dictionary<string, object>> rows);
}

Below I provided the sample implementation of UpdateRows in PHP that iterates through nested dictionaries (first level called ‘rows’ and second level called ‘properties’):

class StoreService
{
    function UpdateRows($params)
    {
        //iterate through rows
        foreach ($params->rows as $row)
        {
            echo $row->Key . "<br>";

            $props = $row->Value->KeyValueOfstringanyType;
            
            //workaround: if $props contains one element it is stdClass insted of array 
            if (!is_array($props))
            {
                $props = array($props);
            }

            //iterate through properties
            foreach ($props as $prop)
            {
                echo $prop->Key . " " . $prop->Value . "<br>";
            }
        }
    }
}

To figure out what is KeyValueOfstringanyType lets take a look at a piece of WSDL generated by Microsoft (line 45):

...
<xs:element name="UpdateRows">
    <xs:complexType>
        <xs:sequence>
            <xs:element minOccurs="0" name="table_name" nillable="true"
                type="xs:string" />
            <xs:element minOccurs="0" name="rows" nillable="true"
                type="q11:ArrayOfKeyValueOfintArrayOfKeyValueOfstringanyTypety7Ep6D1"
                xmlns:q11="http://schemas.microsoft.com/2003/10/Serialization/Arrays" />
        </xs:sequence>
    </xs:complexType>
</xs:element>
...
<xs:complexType
    name="ArrayOfKeyValueOfintArrayOfKeyValueOfstringanyTypety7Ep6D1">
    <xs:annotation>
        <xs:appinfo>
            <IsDictionary
                xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</IsDictionary>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="unbounded"
            name="KeyValueOfintArrayOfKeyValueOfstringanyTypety7Ep6D1">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="Key" type="xs:int" />
                    <xs:element name="Value" nillable="true"
                        type="tns:ArrayOfKeyValueOfstringanyType" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:sequence>
</xs:complexType>
...
<xs:complexType name="ArrayOfKeyValueOfstringanyType">
    <xs:annotation>
        <xs:appinfo>
            <IsDictionary
                xmlns="http://schemas.microsoft.com/2003/10/Serialization/">true</IsDictionary>
        </xs:appinfo>
    </xs:annotation>
    <xs:sequence>
        <xs:element minOccurs="0" maxOccurs="unbounded"
            name="KeyValueOfstringanyType">
            <xs:complexType>
                <xs:sequence>
                    <xs:element name="Key" nillable="true" type="xs:string" />
                    <xs:element name="Value" nillable="true" type="xs:anyType" />
                </xs:sequence>
            </xs:complexType>
        </xs:element>
    </xs:sequence>
</xs:complexType>
...

So at PHP side we have something like this:

[rows] => stdClass Object        
(            
    [KeyValueOfintArrayOfKeyValueOfstringanyTypety7Ep6D1] => stdClass Object                (                    
    [Key] => 156
    [Value] => stdClass Object
    (                            
        [KeyValueOfstringanyType] => stdClass Object
            (                                    
                [Key] => Sku                                    
                [Value] => Some Value                                
            )                        
    )
)

By default WCF allows to pass only known types as object. If I pass a complex type, for example int[] I get the exception with the following message:

“There was an error while trying to serialize parameter http://tempuri.org/:rows. The InnerException message was ‘Type ‘System.Int32[]’ with data contract name ‘ArrayOfint:http://schemas.microsoft.com/2003/10/Serialization/Arrays’ is not expected. Consider using a DataContractResolver or add any types not known statically to the list of known types – for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.’.   Please see InnerException for more details.”

Looks like it is impossible to pass Dictionary<string, object> from PHP to C# because PHP sends values without type information:

<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:ns2="http://tempuri.org/">
  <s:Header xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" />
  <SOAP-ENV:Body>
    <ns2:UpdateProductResponse>
      <ns2:UpdateProductResult>
        <ns1:KeyValueOfstringanyType>
          <ns1:Key>Title</ns1:Key>
          <ns1:Value>SomeTitleValue</ns1:Value>
        </ns1:KeyValueOfstringanyType>
      </ns2:UpdateProductResult>
    </ns2:UpdateProductResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

As opposed to PHP, C# includes i:type attribute to each value:

<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <Action s:mustUnderstand="1" xmlns="http://schemas.microsoft.com/ws/2005/05/addressing/none">http://tempuri.org/IStore/UpdateProduct</Action>
  </s:Header>
  <s:Body>
    <UpdateProduct xmlns="http://tempuri.org/">
      <id>10</id>
      <props xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
        <d4p1:KeyValueOfstringanyType>
          <d4p1:Key>Name</d4p1:Key>
          <d4p1:Value xmlns:d6p1="http://www.w3.org/2001/XMLSchema" i:type="d6p1:string">Gig Mac1</d4p1:Value>
        </d4p1:KeyValueOfstringanyType>
        <d4p1:KeyValueOfstringanyType>
          <d4p1:Key>InStock</d4p1:Key>
          <d4p1:Value xmlns:d6p1="http://www.w3.org/2001/XMLSchema" i:type="d6p1:int">39</d4p1:Value>
        </d4p1:KeyValueOfstringanyType>
        <d4p1:KeyValueOfstringanyType>
          <d4p1:Key>ExpiryDate</d4p1:Key>
          <d4p1:Value xmlns:d6p1="http://www.w3.org/2001/XMLSchema" i:type="d6p1:dateTime">2013-03-17T16:35:20.1089466+04:00</d4p1:Value>
        </d4p1:KeyValueOfstringanyType>
      </props>
    </UpdateProduct>
  </s:Body>
</s:Envelope>

Leave a Reply

Your email address will not be published. Required fields are marked *