Calling WCF Services from Python with Complex Parameter Serialization

Setting Up the WCF Service

Defining Service Contracts

The IServiceDemo.cs file defines the service contract along with five operation contracts that handle different parameter types for testing purposes. Two custom data contracts are also defined for data exchange.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfServiceDemo
{
    [ServiceContract]
    public interface IServiceDemo
    {
        [OperationContract]
        string GetSimpleData(string value);

        [OperationContract]
        List<Item> GetListData(List<Item> items);

        [OperationContract]
        Item GetModelData(Item item);

        [OperationContract]
        Dictionary<string, string> GetDicData(Dictionary<string, string> dic);

        [OperationContract]
        Dictionary<string, Dictionary<string, int>[]> GetDicDicData(Dictionary<string, Dictionary<string, int>[]> dic);
    }

    [DataContract]
    public class ItemMenu
    {
        [DataMember]
        public string Name { get; set; }
        [DataMember]
        public string Value { get; set; }
    }

    [DataContract]
    public class Item
    {
        [DataMember]
        public List<ItemMenu> ItemMenus { get; set; }
    }
}

The service implementation in ServiceDemo.svc.cs returns the input parameters to verify correct serialization:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.ServiceModel.Web;
using System.Text;

namespace WcfServiceDemo
{
    public class ServiceDemo : IServiceDemo
    {
        public string GetSimpleData(string value)
        {
            return value;
        }

        public List<Item> GetListData(List<Item> items)
        {
            return items;
        }

        public Item GetModelData(Item item)
        {
            return item;
        }

        public Dictionary<string, string> GetDicData(Dictionary<string, string> dic)
        {
            return dic;
        }

        public Dictionary<string, Dictionary<string, int>[]> GetDicDicData(Dictionary<string, Dictionary<string, int>[]> dic)
        {
            return dic;
        }
    }
}

Configuring the Host

A console application serves as the service host for easy deployment during testing. The App.config file configures the WCF service with basicHttpBinding:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="WcfServiceDemo.ServiceDemo" behaviorConfiguration="ServiceDemoBehavior">
        <endpoint address="" contract="WcfServiceDemo.IServiceDemo" binding="basicHttpBinding"></endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8001/ServiceDemo/"></add>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceDemoBehavior">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
</configuration>

Starting the Service

static void Main(string[] args)
{
    using (ServiceHost host = new ServiceHost(typeof(WcfServiceDemo.ServiceDemo)))
    {
        host.Open();
        Console.WriteLine("Service started successfully");
        Console.Read();
    }
}

Common Isue: If you encounter "HTTP could not register URL http://+:8001/ServiceDemo/" error, run Visual Studio as Administrator.

Working with Suds Client

Discovering the WSDL Structure

First, instantiate the suds client and print its contents to understand the available methods and parameter types:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from suds.client import Client

if __name__ == '__main__':
    client = Client('http://localhost:8001/ServiceDemo/?singleWsdl')
    print(client)
    result = client.service.GetSimpleData('123')
    print(result)

The output reveals the available methods and type definitions:

Service (ServiceDemo) tns="http://tempuri.org/"
   Prefixes (4)
      ns0 = "http://schemas.datacontract.org/2004/07/WcfServiceDemo"
      ns1 = "http://schemas.microsoft.com/2003/10/Serialization/"
      ns2 = "http://schemas.microsoft.com/2003/10/Serialization/Arrays"
      ns3 = "http://tempuri.org/"
   Ports (1):
      (BasicHttpBinding_IServiceDemo)
         Methods (5):
            GetDicData(ns2:ArrayOfKeyValueOfstringstring dic)
            GetDicDicData(ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 dic)
            GetListData(ns0:ArrayOfItem items)
            GetModelData(ns0:Item item)
            GetSimpleData(xs:string value)
         Types (11):
            ns2:ArrayOfArrayOfKeyValueOfstringint
            ns0:ArrayOfItem
            ns0:ArrayOfItemMenu
            ...

Handling Complex Parameter Serialization

Primitive types can be passed directly, but complex types require careful construction. The key is to build objects from the leaf level upward. Use client.factory.create() to instantiate parameters, starting with the innermost types.

For deeply nested structures like GetDicDicData, explore the type hierarchy first:

print(client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1'))
print(client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1'))
print(client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint'))
print(client.factory.create('ns2:ArrayOfKeyValueOfstringint'))
print(client.factory.create('ns2:KeyValueOfstringint'))

kv_int = client.factory.create('ns2:KeyValueOfstringint')
kv_int.Key = 'test_key'
kv_int.Value = 1

arr_kv_int = client.factory.create('ns2:ArrayOfKeyValueOfstringint')
arr_kv_int.KeyValueOfstringint = [kv_int]

arr_arr_kv_int = client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
arr_arr_kv_int.ArrayOfKeyValueOfstringint = [arr_kv_int]

kv_arr = client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
kv_arr.Key = 'outer_key'
kv_arr.Value = arr_arr_kv_int

outer_arr = client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
outer_arr.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = kv_arr

Complete implementation showing all parameter types:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from suds.client import Client

if __name__ == '__main__':
    client = Client('http://localhost:8001/ServiceDemo/?singleWsdl')
    
    # Simple type
    result = client.service.GetSimpleData('123')
    print(result)

    # Custom model type
    menu_item = client.factory.create('ns0:ItemMenu')
    menu_item.Name = 'TestName'
    menu_item.Value = 'TestValue'
    
    menu_array = client.factory.create('ns0:ArrayOfItemMenu')
    menu_array.ItemMenu = [menu_item, menu_item]
    
    item = client.factory.create('ns0:Item')
    item.ItemMenus = menu_array
    
    result = client.service.GetModelData(item)
    print(result)
    print(result.ItemMenus.ItemMenu[0].Name)
    print(result.ItemMenus.ItemMenu[0].Value)

    # List of custom models
    items_array = client.factory.create('ns0:ArrayOfItem')
    items_array.Item = [item, item]
    result = client.service.GetListData(items_array)
    print(result)
    print(result.Item[0].ItemMenus.ItemMenu[0].Name)

    # Dictionary type
    kv_string = client.factory.create('ns2:KeyValueOfstringstring')
    kv_string.Key = '01'
    kv_string.Value = 'test_value'
    
    kv_array = client.factory.create('ns2:ArrayOfKeyValueOfstringstring')
    kv_array.KeyValueOfstringstring = [kv_string]
    
    result = client.service.GetDicData(kv_array)
    print(result.KeyValueOfstringstring[0].Key)
    print(result.KeyValueOfstringstring[0].Value)

    # Nested dictionary (leaf-level construction)
    kv_inner = client.factory.create('ns2:KeyValueOfstringint')
    kv_inner.Key = 'inner_key'
    kv_inner.Value = 42
    
    arr_inner = client.factory.create('ns2:ArrayOfKeyValueOfstringint')
    arr_inner.KeyValueOfstringint = [kv_inner]
    
    arr_arr_inner = client.factory.create('ns2:ArrayOfArrayOfKeyValueOfstringint')
    arr_arr_inner.ArrayOfKeyValueOfstringint = [arr_inner]
    
    kv_outer = client.factory.create('ns2:KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
    kv_outer.Key = 'outer_key'
    kv_outer.Value = arr_arr_inner
    
    outer_container = client.factory.create('ns2:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
    outer_container.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1 = kv_outer
    
    result = client.service.GetDicDicData(outer_container)
    print(result.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1[0].Key)
    print(result.KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1[0].Value)

Troubleshooting wsHttpBinding

When configuring the endpoint with wsHttpBinding, suds may fail with this error:

<endpoint address="" contract="WcfServiceDemo.IServiceDemo" binding="wsHttpBinding"></endpoint>

Exception: (415, u"Cannot process the message because the content type 'text/xml; charset=utf-8' was not the expected type 'application/soap+xml; charset=utf-8'.")

Suds does not support wsHttpBinding. Use zeep instead for this binding type.

Using Zeep Client Library

WCF Service Configuration for Zeep

When using zeep, configure wsHttpBinding with security mode set to None:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <wsHttpBinding>
        <binding name="WSHttpBinding_IWCFService">
          <security mode="None">
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="WcfServiceDemo.ServiceDemo" behaviorConfiguration="ServiceDemoBehavior">
        <endpoint address="" contract="WcfServiceDemo.IServiceDemo" 
                  binding="wsHttpBinding" 
                  bindingConfiguration="WSHttpBinding_IWCFService"></endpoint>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8021/ServiceDemo/"></add>
          </baseAddresses>
        </host>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceDemoBehavior">
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <startup>
    <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
  </startup>
</configuration>

Parameter Serialization with Zeep

Zeep provides a cleaner API for creating complex types using client.get_type(). Dictionary parameters can use dictionary literal syntax:

# -*- coding: utf-8 -*-
import sys
reload(sys)
sys.setdefaultencoding('utf-8')

from zeep import Client

if __name__ == '__main__':
    client = Client('http://localhost:8021/ServiceDemo/?singleWsdl')
    print(client)
    print(client.namespaces)

    # Basic type
    print(client.service.GetSimpleData('abc'))

    # Custom model type
    menu_type = client.get_type('ns2:ItemMenu')
    menu_instance = menu_type(Name='user_name', Value='user_value')
    
    arr_menu_type = client.get_type('ns2:ArrayOfItemMenu')
    menu_list = arr_menu_type(ItemMenu=[menu_instance, menu_instance])
    
    item_type = client.get_type('ns2:Item')
    item_instance = item_type(ItemMenus=menu_list)
    
    print(client.service.GetModelData(item_instance))

    # List of custom models
    arr_item_type = client.get_type('ns2:ArrayOfItem')
    items_collection = arr_item_type(Item=[item_instance, item_instance])
    print(client.service.GetListData(items_collection))

    # Dictionary type - using dict literals
    kv_str_type = client.get_type('ns3:ArrayOfKeyValueOfstringstring')
    dict_data = kv_str_type(KeyValueOfstringstring=[{'Key': 'a', 'Value': 'aaa'}, {'Key': 'b', 'Value': 'bbb'}])
    print(dict_data)

    # Nested dictionary types
    kv_int_type = client.get_type('ns3:ArrayOfKeyValueOfstringint')
    inner_dict = kv_int_type(KeyValueOfstringint=[{'Key': 'a', 'Value': 1}, {'Key': 'b', 'Value': 2}])
    
    arr_arr_type = client.get_type('ns3:ArrayOfArrayOfKeyValueOfstringint')
    nested_arr = arr_arr_type(ArrayOfKeyValueOfstringint=[inner_dict])
    
    outer_dict_type = client.get_type('ns3:ArrayOfKeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1')
    complex_dict = outer_dict_type(KeyValueOfstringArrayOfArrayOfKeyValueOfstringintty7Ep6D1={'Key': 'c', 'Value': nested_arr})
    
    result = client.service.GetDicDicData(complex_dict)
    print(result)
    print(result[0].Value.ArrayOfKeyValueOfstringint[0].KeyValueOfstringint[0].Key)

To view detailed WSDL information:

print(client.wsdl.dump())

Tags: python WCF SOAP serialization Suds

Posted on Wed, 08 Jul 2026 16:50:59 +0000 by Boxerman