Reputation:
I am working on a Windows Store application.
I have the following manually created Fault class:
[XmlRoot(Namespace = "http://schemas.xmlsoap.org/soap/envelope/", ElementName = "Fault")]
public partial class SoapFault
{
[XmlElement(Form = XmlSchemaForm.Unqualified, ElementName = "faultcode")]
public String FaultCode { get; set; }
[XmlElement(Form = XmlSchemaForm.Unqualified, ElementName = "faultstring")]
public String FaultDescription { get; set; }
[XmlElement(Form = XmlSchemaForm.Unqualified, ElementName = "detail")]
public InnerException[] Detail { get; set; }
}
[XmlType(Namespace = "http://my.namespace.com", TypeName = "InnerException")]
public partial class InnerException
{
[XmlElement(Form = XmlSchemaForm.Unqualified, ElementName = "message")]
public String Message { get; set; }
}
This is the reply of the server i am trying to read:
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/"><env:Body><env:Fault><faultcode>env:Server</faultcode><faultstring>internal error</faultstring><detail><ns2:InnerException xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://ns1.my.namespace.com" xmlns:ns2="http://my.namespace.com" xmlns:ns3="http://ns3.my.namespace.com"><message>internal error</message></ns2:InnerException ></detail></env:Fault></env:Body></env:Envelope>
The way i am trying to read it is as follows:
using (XmlReader reader = XmlReader.Create(await response.Content.ReadAsStreamAsync()))
{
string SoapNamespace = "http://schemas.xmlsoap.org/soap/envelope/";
try
{
var serializer = new XmlSerializer(typeof(SoapFault));
reader.ReadStartElement("Envelope", SoapNamespace);
reader.ReadStartElement("Body", SoapNamespace);
var fault = serializer.Deserialize(reader) as SoapFault;
reader.ReadEndElement();
reader.ReadEndElement();
}
catch(Exception ex)
{
throw new Exception("Exception was thrown:" + ex.Message);
}
}
I have tried adding namespaces, changing the XmlElement Attributes, but i always end up with the Detail property in the SoapFault being NULL. When i change the type to object, i at least get a XmlNode set which contains the data.
What must i change in this code to get the correct class instantiated on serialization?
Please note: I am unfortunatly forced to create the calls by hand and cannot use auto-generated code.
Upvotes: 5
Views: 14214
Reputation: 14938
Heavily inspired by How to Deserialize XML document, I think this should do the trick.
I generated the class as follows:
This resulted in:
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System.Xml.Serialization;
//
// This source code was auto-generated by xsd, Version=4.0.30319.1.
//
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://schemas.xmlsoap.org/soap/envelope/", IsNullable=false)]
public partial class Envelope {
private EnvelopeBody[] itemsField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Body")]
public EnvelopeBody[] Items {
get {
return this.itemsField;
}
set {
this.itemsField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public partial class EnvelopeBody {
private EnvelopeBodyFault[] faultField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute("Fault")]
public EnvelopeBodyFault[] Fault {
get {
return this.faultField;
}
set {
this.faultField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public partial class EnvelopeBodyFault {
private string faultcodeField;
private string faultstringField;
private EnvelopeBodyFaultDetail detailField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string faultcode {
get {
return this.faultcodeField;
}
set {
this.faultcodeField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string faultstring {
get {
return this.faultstringField;
}
set {
this.faultstringField = value;
}
}
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public EnvelopeBodyFaultDetail detail {
get {
return this.detailField;
}
set {
this.detailField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://schemas.xmlsoap.org/soap/envelope/")]
public partial class EnvelopeBodyFaultDetail {
private InnerException innerExceptionField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Namespace="http://my.namespace.com")]
public InnerException InnerException {
get {
return this.innerExceptionField;
}
set {
this.innerExceptionField = value;
}
}
}
/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://my.namespace.com")]
[System.Xml.Serialization.XmlRootAttribute(Namespace="http://my.namespace.com", IsNullable=false)]
public partial class InnerException {
private string messageField;
/// <remarks/>
[System.Xml.Serialization.XmlElementAttribute(Form=System.Xml.Schema.XmlSchemaForm.Unqualified)]
public string message {
get {
return this.messageField;
}
set {
this.messageField = value;
}
}
}
Yes, it is auto generated, but I figured that would be the easiest way to create a class that you would be able to deserialize. Which you can do like this:
XmlDocument document = new XmlDocument();
document.Load(Server.MapPath("~/Data/reply.xml"));
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Envelope));
Envelope envelope = (Envelope)serializer.Deserialize(new StringReader(document.OuterXml));
Which correctly picked up the Detail
property. In your case you would need to use the XmlReader
like you were doing before instead of my new StringReader(document.OuterXml)
, and wrap it in a using block, etc.
Keep in mind that some of the property names of the deserialized object won't match up exactly with what you had in your SoapFault
class (it's now called Envelope
, for example), and some of the properties are expressed as arrays rather than single objects, but you could probably tweak the Xsd to suit if need be.
Upvotes: 7