2

After reading this article:

Improve Web Service Interoperability with XML Message Design

I decided to have a go at importing our existing message design schema into the WSDL for our new SOAP service.

This is all in VS2008 and .NET 3.5, Altova XMLSpy 2009 was used for schema and wsdl design.

So I did the following:

  1. Write Custom WSDL, importing our XSD interface spec.
  2. Generate server stub code using WSDL.EXE
  3. Include the XSD in the web service project
  4. Use Linq To Xsd to generate the strongly typed XDocument wrappers we use
  5. Manually edit the generated stub code to remove the redundant auto generated wrapper classes
  6. Manually edit the stub code to map the types in the WSDL to the Linq2Xsd generated wrappers
  7. Disable WSDL generation and explicitly reference the custom WSDL with the WebServiceBindingAttribute
  8. Create a win forms test rig using regular web references

OK, here's the problem:

The SOAP request works perfectly, serialization and de serialization of the parameters works. The SOAP response in the problem, the serialization is creating incorrectly formed Xml and the client de serialization is just failing to work.

Here's the SOAP response:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <GetCameraLocationsTableResponse xmlns="http://blah">
            <GetCameraLocationsTableResponse Version="1.0rc2">
                <Response>
                    <Success>true</Success>
                </Response>
                <CameraLocationsTable/>
            </GetCameraLocationsTableResponse>
        </GetCameraLocationsTableResponse>
    </soap:Body>
</soap:Envelope>

Note the nested GetCameraLocationsTableResponse elements, this is wrong. I've confirmed that our XDocument wrapper creates the inner GetCameraLocationsTableResponse and something in the inner workings of the SOAP message generator adds the outer GetCameraLocationsTableResponse It should be:

<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <soap:Body>
        <GetCameraLocationsTableResponse Version="1.0rc2" xmlns="http://blah">
            <Response>
                <Success>true</Success>
            </Response>
            <CameraLocationsTable/>
        </GetCameraLocationsTableResponse>
    </soap:Body>
</soap:Envelope>

So I'm stumped, the client proxy code serializes correctly but the server proxy code does not. Of course, it's the server code I've mucked about with!

Names have been changed to Blah to protech the innocent.

For reference, here's my edited server side proxy code:

[System.CodeDom.Compiler.GeneratedCodeAttribute("wsdl", "2.0.50727.3038")]
[System.Web.Services.WebServiceBindingAttribute(Name = "Blah", Namespace = "http://Blah", Location = @"http://localhost:57264/wsdl/Blah.wsdl")]
public interface IBlah
{

    [System.Web.Services.WebMethodAttribute()]
    [SoapLogger]
    [System.Web.Services.Protocols.SoapDocumentMethodAttribute("", Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]
    [return: System.Xml.Serialization.XmlElementAttribute("GetCameraLocationsTableResponse", Namespace="http://Blah")]
    GetCameraLocationsTableResponse GetCameraLocationsTable([System.Xml.Serialization.XmlElementAttribute(Namespace="http://Blah")] NativeCaseInterfaceDelegateRequest NativeCaseInterfaceDelegateRequest);
}

Here's the code I wrote to implement the above as a web service:

[WebService(Namespace = "http://blah")]
public class Blah : WebService, IBlah
{
    #region IBlah Members

    public GetCameraLocationsTableResponse GetCameraLocationsTable(Blah BlahRequest)
    {
        return BlahTools.GetCameraLocationsTable(BlahRequest);
    }

    #endregion
}

Using the system.diagnostics switch from here I managed to extract the auto generated serialization code:

    protected override void Serialize(object objectToSerialize, System.Xml.Serialization.XmlSerializationWriter writer) {
        ((XmlSerializationWriter1)writer).Write3_Item((object[])objectToSerialize);
    }

    public void Write3_Item(object[] p) {
        WriteStartDocument();
        TopLevelElement();
        int pLength = p.Length;
        if (pLength > 0) {
            WriteSerializable((System.Xml.Serialization.IXmlSerializable)((global::blah.GetCameraLocationsTableResponse)p[0]), @"GetCameraLocationsTableResponse", @"http://blah", false, true);
        }
    }

Here's the custom WSDL

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:sis="http://Blah" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" targetNamespace="http://Blah">
    <wsdl:import namespace="http://Blah" location="Blah.xsd"/>
    <wsdl:message name="BlahRequestMessage">
        <wsdl:part name="in" element="sis:BlahRequest"/>
    </wsdl:message>
    <wsdl:message name="GetCameraLocationsTableResponseMessage">
        <wsdl:part name="out" element="sis:GetCameraLocationsTableResponse"/>
    </wsdl:message>
    <wsdl:portType name="BlahPort">
        <wsdl:operation name="GetCameraLocationsTable">
            <wsdl:input message="sis:BlahRequestMessage"/>
            <wsdl:output message="sis:GetCameraLocationsTableResponseMessage"/>
        </wsdl:operation>
    </wsdl:portType>
    <wsdl:binding name="Blah" type="sis:BlahPort">
        <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
        <wsdl:operation name="GetCameraLocationsTable">
            <soap:operation soapAction="" style="document"/>
            <wsdl:input>
                <soap:body use="literal"/>
            </wsdl:input>
            <wsdl:output>
                <soap:body use="literal"/>
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>
</wsdl:definitions>

Thanks for reading,

James.

RoboJ1M
  • 1,590
  • 2
  • 27
  • 34

0 Answers0