By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
458,020 Members | 1,297 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 458,020 IT Pros & Developers. It's quick & easy.

Problem generating client proxy with complex return types

P: n/a
Dear all,
I am having trouble generating a client proxy for a webservice whose
methods return a "complex" type. The type is complex in that it is a
class whose members are a mix of primitive types and of more elaborate
classes implementing IXmlSerializable. The resulting WSDL file for the
webservice has two separate schemas in its <types> sections, and the
client proxy (generated with wsdl.exe) is missing the definitions of
the IXmlSerializable types. More details follow.

I could reproduce the problem with a simple example. The webservice is
called WSTest.Service1 @ http://localhost/WSTest/Service1.asmx, and it
has a method called HelloWorld.
Here is the Service1.asmx file (hope the formatting won't go wild):
------------------------------------------------
using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Web;
using System.Web.Services;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;

namespace WSTest
{
// This is the IXmlSerializable derivative, member of the webmethod's
actual return type
public class WSTestDetail: IXmlSerializable
{
// Class data
public string Code;
public string Description;

// IXmlSerializable implementation
public void WriteXml (XmlWriter writer)
{
// ...
}

public void ReadXml (XmlReader reader)
{
// ...
}

public XmlSchema GetSchema ()
{
XmlSchemaSequence seq = new XmlSchemaSequence ();
XmlSchemaElement element;

element = new XmlSchemaElement ();
element.Name = "Code";
element.SchemaTypeName = new XmlQualifiedName ("string",
"http://www.w3.org/2001/XMLSchema");
element.MinOccurs = 0;
element.MaxOccurs = 1;
seq.Items.Add (element);

element = new XmlSchemaElement ();
element.Name = "Description";
element.SchemaTypeName = new XmlQualifiedName ("string",
"http://www.w3.org/2001/XMLSchema");
element.MinOccurs = 0;
element.MaxOccurs = 1;
seq.Items.Add (element);

XmlSchemaComplexType type = new XmlSchemaComplexType ();
type.Particle = seq;
type.Name = "WSTestDetailType";

XmlSchemaElement root = new XmlSchemaElement ();
root.Name = "WSTestDetail";
root.SchemaTypeName = new XmlQualifiedName ("WSTestDetailType",
"http://www.testme.org/types");

XmlSchema schema = new XmlSchema ();
schema.Id = "WSTestDetailSchema";
schema.TargetNamespace = "http://www.testme.org/types";

schema.Namespaces.Add ("xs", "http://www.w3.org/2001/XMLSchema");

schema.Items.Add (type);
schema.Items.Add (root);

return schema;
}
}

// This is the return type of the webmethod
public class WSTestResult
{
public WSTestDetail Detail;
public string Status;
}

/// <summary>
/// Summary description for Service1.
/// </summary>
[WebService(Namespace="http://www.testme.org/ws")]
public class Service1 : System.Web.Services.WebService
{
public Service1()
{
InitializeComponent();
}

#region Component Designer generated code
// ...
#endregion

[WebMethod]
public WSTestResult HelloWorld()
{
return new WSTestResult ();
}
}
}
------------------------------------------------

The WSDL document (Service1.wsdl):
------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<wsdl:definitions xmlns:s1="http://www.testme.org/types"
xmlns:http="http://schemas.xmlsoap.org/wsdl/http/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/"
xmlns:s="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://www.testme.org/ws"
xmlns:tm="http://microsoft.com/wsdl/mime/textMatching/"
xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/"
targetNamespace="http://www.testme.org/ws"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/">
<wsdl:types>
<s:schema elementFormDefault="qualified"
targetNamespace="http://www.testme.org/ws">
<s:import namespace="http://www.testme.org/types" />
<s:element name="HelloWorld">
<s:complexType />
</s:element>
<s:element name="HelloWorldResponse">
<s:complexType>
<s:sequence>
<s:element minOccurs="0" maxOccurs="1"
name="HelloWorldResult" type="tns:WSTestResult" />
</s:sequence>
</s:complexType>
</s:element>
<s:complexType name="WSTestResult">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="Detail">
<s:complexType>
<s:sequence>
<s:any namespace="http://www.testme.org/types" />
</s:sequence>
</s:complexType>
</s:element>
<s:element minOccurs="0" maxOccurs="1" name="Status"
type="s:string" />
</s:sequence>
</s:complexType>
</s:schema>
<s:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.testme.org/types" id="WSTestDetailSchema">
<xs:complexType name="WSTestDetailType">
<xs:sequence>
<xs:element minOccurs="0" maxOccurs="1" name="Code"
type="xs:string" />
<xs:element minOccurs="0" maxOccurs="1" name="Description"
type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="WSTestDetail" type="s1:WSTestDetailType" />
</s:schema>
</wsdl:types>
<wsdl:message name="HelloWorldSoapIn">
<wsdl:part name="parameters" element="tns:HelloWorld" />
</wsdl:message>
<wsdl:message name="HelloWorldSoapOut">
<wsdl:part name="parameters" element="tns:HelloWorldResponse" />
</wsdl:message>
<wsdl:portType name="Service1Soap">
<wsdl:operation name="HelloWorld">
<wsdl:input message="tns:HelloWorldSoapIn" />
<wsdl:output message="tns:HelloWorldSoapOut" />
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="Service1Soap" type="tns:Service1Soap">
<soap:binding transport="http://schemas.xmlsoap.org/soap/http"
style="document" />
<wsdl:operation name="HelloWorld">
<soap:operation soapAction="http://www.testme.org/ws/HelloWorld"
style="document" />
<wsdl:input>
<soap:body use="literal" />
</wsdl:input>
<wsdl:output>
<soap:body use="literal" />
</wsdl:output>
</wsdl:operation>
</wsdl:binding>
<wsdl:service name="Service1">
<documentation xmlns="http://schemas.xmlsoap.org/wsdl/" />
<wsdl:port name="Service1Soap" binding="tns:Service1Soap">
<soap:address location="http://localhost/WSTest/Service1.asmx" />
</wsdl:port>
</wsdl:service>
</wsdl:definitions>
------------------------------------------------

As you can see, with WSTestDetail providing its own GetSchema()
implementation, we have 2 <schema>'s in the WSDL.
WSTestResult.Detail has a strange declaration: instead of the expected
<s:element name="Detail" type="s1:WSTestDetailType">, I get a
complexType/sequence/any specification. Why not; the 2nd schema has
only one toplevel element so I guess that's fine and leads to no
ambiguities.
Now the client proxy (wsdl.exe Service1.wsdl):
------------------------------------------------
using System.Diagnostics;
using System.Xml.Serialization;
using System;
using System.Web.Services.Protocols;
using System.ComponentModel;
using System.Web.Services;
/// <remarks/>
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("c ode")]
[System.Web.Services.WebServiceBindingAttribute(Nam e="Service1Soap",
Namespace="http://www.testme.org/ws")]
public class Service1 :
System.Web.Services.Protocols.SoapHttpClientProtoc ol {

/// <remarks/>
public Service1() {
this.Url = "http://localhost/WSTest/Service1.asmx";
}

/// <remarks/>

[System.Web.Services.Protocols.SoapDocumentMethodAt tribute("http://www.testme.org/ws/HelloWorld",
RequestNamespace="http://www.testme.org/ws",
ResponseNamespace="http://www.testme.org/ws",
Use=System.Web.Services.Description.SoapBindingUse .Literal,
ParameterStyle=System.Web.Services.Protocols.SoapP arameterStyle.Wrapped)]
public WSTestResult HelloWorld() {
object[] results = this.Invoke("HelloWorld", new object[0]);
return ((WSTestResult)(results[0]));
}

/// <remarks/>
public System.IAsyncResult BeginHelloWorld(System.AsyncCallback
callback, object asyncState) {
return this.BeginInvoke("HelloWorld", new object[0], callback,
asyncState);
}

/// <remarks/>
public WSTestResult EndHelloWorld(System.IAsyncResult asyncResult)
{
object[] results = this.EndInvoke(asyncResult);
return ((WSTestResult)(results[0]));
}
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(Namespac e="http://www.testme.org/ws")]
public class WSTestResult {

/// <remarks/>
public WSTestDetailSchema Detail;

/// <remarks/>
public string Status;
}
------------------------------------------------

WSTestResult.Detail's type is named WSTestDetailSchema (I would have
expected WSTestDetailType), but again, why not. The missing type
declaration for WSTestDetailSchema is much more annoying as this makes
the WSDL useless for clients.

Is there any reason why this is happening? Why do I have 2 schemas in
my WSDL? How can I ask that WSTestResult.Detail be of type
s1:WSTestDetailType in the WSDL? Since the class in Service1.asmx
implements IXmlSerializable, I cannot use Xml serialization attributes
like XmlType. The interface is needed because we want to have fine
control on the serialization.
I hope the question is not too stupid. The thing is, I'm not too
familiar with schemas, namespaces and such. Also, we're in 1.1 and
IXmlSerializable is not very well documented.

Thanks in advance!

Best regards,
Thomas

Feb 22 '06 #1
Share this Question
Share on Google+
1 Reply


P: n/a
Seems that wsdl.exe does not like IXmlSerializable. I could get around
this by intercepting the "?wsdl" call in Application_BeginRequest (),
getting the incorrect ServiceDescription from there (with
ServiceDescriptionReflector), and altering
ServiceDescription.Types.Schemas.
The idea is to collapse all schemas therein into the first one, replace
the complexType/sequence/any with a type="..." attribute, and fix up
all namespaces references.

Not very elegant but it works very well... Also, the default
documentation/test page for the service is not affected, as would have
been the case if I had added a <wsdlHelpGenerator> tag in Web.config.

Thomas

Feb 24 '06 #2

This discussion thread is closed

Replies have been disabled for this discussion.