Hello,
I'm trying to build a C# client to consume an AXIS Web Service (running SOAP
over HTTP). The Web Service encodes
full server-side exception traces in the Soap Fault > Detail element using
complex type structures declared in
the WSDL file.
I have had absolutely no luck working out how I can deserialize the custom
server exception object out of the
detail element.
I have tried both SoapFormatter, and XmlSerializer with absolutely no luck.
try
{
<<<< e.g. login operation >>>>
}
catch (System.Web.Services.Protocols.SoapException e)
{
XmlReader reader = null;
XmlWriter writer = null;
MemoryStream mem = new MemoryStream();
FdkException fe = null;
try
{
reader = new XmlNodeReader(e.Detail.FirstChild);
writer = new XmlTextWriter(mem, System.Text.Encoding.UTF8);
writer.WriteNode(reader,true);
writer.Flush();
mem.Position = 0;
<<<< Add deserialization code here >>>>
fe = (FdkException) ....
}
catch (Exception ex)
{
System.Console.WriteLine(ex.toString());
throw;
}
}
The first deserialization mechansim I tried was using
System.Runtime.Serialization.Formatters.Soap.SoapF ormatter
SoapFormatter sf = new SoapFormatter();
sf.Binder = new FdkExceptionDeserializationBinder();
fe = (FdkException) sf.Deserialize(mem);
With FdkExceptionDeserializationBinder.cs looking like the following:
public class FdkExceptionDeserializationBinder :
System.Runtime.Serialization.SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName.Equals(http://xmlns.mycompany.com/app/ws))
{
switch (typeName)
{
case FdkException : return typeof(FdkException); break;
case ArrayOfFdkExceptionEntry : return typeof(FdkExceptionEntry[]);
break;
}
}
return Type.GetType(String.Format({0}, {1}, typeName, assemblyName));
}
}
This deserialization approach resulted in an exception:
Exception Type: System.Runtime.Serialization.SerializationExceptio n
Message: No Top Object
Source: System.Runtime.Serialization.Formatters.Soap
The second deserialization mechanism I tried was using XmlSerializer:
XmlTypeMapping myMapping = (new
SoapReflectionImporter().ImportTypeMapping(typeof( FdkException)));
XmlSerializer serializer = new XmlSerializer(myMapping);
fe = (FdkException) serializer.Deserialize(mem);
I tried settign SOAP options in the .NET genererated FdkException class such
as the following before the class
definition:
I set SoapType options in the FdkException class:
using System;
using System.Xml.Serialization;
....
[Serializable]
[SoapTypeAttribute(Namespace="http://xmlns.mycompany.com/app/ws",
TypeName=fault)]
public class FdkException
{
public string errorCode;
public FdkExceptionEntry[] exceptionEntries;
public string serverStackTraceId;
}
using System;
using System.Xml.Serialization;
[Serializable]
[SoapTypeAttribute(Namespace="http://xmlns.mycompany.com/app/ws",
TypeName=FdkExceptionEntry)]
public class FdkExceptionEntry
{
public string errorCode;
public long id;
public string serverStackTraceId;
}
I got the following exception:
Message: There is an error in XML Document (1,541).
Exception Type: System.InvalidOperationException
Source: System.Xml
Inner Exception:
Message: Cannot assign object of type System.Xml.XmlNode[] to an object of
type FdkException
Exception Type: System.InvalidCastException
Below is the SOAP message returned from the server on an invalid logon
attempt (including Fault):-
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<soapenv:Fault>
<faultcode>soapenv:Server.userException</faultcode>
<faultstring>AccessDenied</faultstring>
<detail>
<ns1:fault xsi:type="ns1:FdkException"
xmlns:ns1="http://xmlns.mycompany.com/app/ws">
<errorCode xsi:type="soapenc:string"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/">AccessDenied -
Invalid Credentials</errorCode>
<exceptionEntries xsi:type="ns1:ArrayOfFdkExceptionEntry"
xsi:nil="true"/>
<serverStackTraceId xsi:type="soapenc:string"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"/>
</ns1:fault>
</detail>
</soapenv:Fault>
</soapenv:Body>
</soapenv:Envelope>
Below is pieces of the WSDL file ripped out relating to the FdkException
<wsdl:types>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://xmlns.mycompany.com/app/ws">
<import namespace="http://schemas.xmlsoap.org/soap/encoding/"/>
<complexType name="FdkExceptionEntry">
<sequence>
<element name="errorCode" nillable="true" type="xsd:string"/>
<element name="id" type="xsd:long"/>
<element name="serverStackTraceId" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
<complexType name="ArrayOfFdkExceptionEntry">
<complexContent>
<restriction base="soapenc:Array">
<attribute ref="soapenc:arrayType"
wsdl:arrayType="impl:FdkExceptionEntry[]"/>
</restriction>
</complexContent>
</complexType>
<complexType name="FdkException">
<sequence>
<element name="errorCode" nillable="true" type="xsd:string"/>
<element name="exceptionEntries" nillable="true"
type="impl:ArrayOfFdkExceptionEntry"/>
<element name="serverStackTraceId" nillable="true" type="xsd:string"/>
</sequence>
</complexType>
</schema>
</wsdl:types>
<wsdl:message name="FdkException">
<wsdl:part name="fault" type="impl:FdkException"/>
</wsdl:message>
<wsdl:portType name="RemoteLoginManager">
<wsdl:operation name="login" parameterOrder="username password options
userAttributes">
<wsdl:input name="loginRequest" message="impl:loginRequest"/>
<wsdl:output name="loginResponse" message="impl:loginResponse"/>
<wsdl:fault name="FdkException" message="impl:FdkException"/>
</wsdl:operation>
</wsdl:portType>
<wsdl:binding name="RemoteLoginManagerSoapBinding"
type="impl:RemoteLoginManager">
<wsdlsoap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http"/>
<wsdl:operation name="login">
<wsdlsoap:operation/>
<wsdl:input>
<wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://xmlns.mycompany.com/app/ws"/>
</wsdl:input>
<wsdl:output>
<wsdlsoap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://xmlns.mycompany.com/app/ws"/>
</wsdl:output>
<wsdl:fault name="FdkException">
<wsdlsoap:fault name="FdkException" use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://xmlns.mycompany.com/app/ws"/>
</wsdl:fault>
</wsdl:operation>
</wsdl:binding>
Has anyone out there been able to deserialize from SOAP fault detail element?
many thanks,
Matt.