How to Deserialize Xml If the Return Type Could Be an Error or Success Object

How to deserialize XML if the return type could be an Error or Success object

In situations where you have an XML stream containing one of several possible document types, you can construct an XmlSerializer for each type and call XmlSerializer.CanDeserialize(XmlReader) to successively test whether the document can be deserialized into that type. This method does not advance the XmlReader past the root element so it can be called multiple times without re-reading the stream.

For instance, you could introduce the following extension method:

public static partial class XmlSerializerExtensions
{
public static object DeserializePolymorphicXml(this string xml, params Type[] types)
{
using (var textReader = new StringReader(xml))
{
return textReader.DeserializePolymorphicXml(types);
}
}

public static object DeserializePolymorphicXml(this TextReader textReader, params Type[] types)
{
if (textReader == null || types == null)
throw new ArgumentNullException();
var settings = new XmlReaderSettings { CloseInput = false }; // Let caller close the input.
using (var xmlReader = XmlReader.Create(textReader, settings))
{
foreach (var type in types)
{
var serializer = new XmlSerializer(type);
if (serializer.CanDeserialize(xmlReader))
return serializer.Deserialize(xmlReader);
}
}
throw new XmlException("Invalid root type.");
}
}

Then use it as follows:

var xmlBody = result.ResponseBody.DeserializePolymorphicXml(typeof(SuccessResponse), typeof(FailResponse));
if (xmlBody is SuccessResponse)
{
// Handle successful response
}
else if (xmlBody is FailResponse)
{
// Handle failed response
}
else
{
// unknown response
throw new InvalidOperationException("unknown response");
}

Sample fiddle.

XML Deserialize, loading an instance of a saved character

Try following deserializer

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ConsoleApplication1
{
public class Program
{
const string FILENAME = @"c:\temp\test.xml";
public static void Main()
{
XmlReader reader = XmlReader.Create(FILENAME);
XmlSerializer serializer = new XmlSerializer(typeof(Berserker));
Berserker berserker = (Berserker)serializer.Deserialize(reader);

}
}
public class Berserker
{
public string Name { get; set;}
public int Strength { get; set;}
public int Dexterity { get; set;}
public int Wisdom { get; set;}
public int Health { get; set;}
public string Gender { get; set;}
public string CharacterClass { get; set; }
}
}

Error Deserializing Xml to Object - xmlns='' was not expected

Simply take off the Namespace =:

[XmlRoot("register-account"), XmlType("register-account")]
public class RegisterAccountResponse {...}

since your xml doesn't seem to be in an xml-namespace. Also, [Serializable] isn't used by XmlSerializer.

If your xml was using a namespace it would have an xmlns at the root.

Also, to help with callers you could add where T : class, new() (the , new() being the addition) to your Deserialize method, since XmlSerializer demands a public parameterless constructor.

XmlSerializer Deserialize failures

Why are you manually deserializing XML, when you have WSDL ?

If you have WSDL, use the svcutil.exe tool, or the wsdl.exe tool, to generate proxy classes and DTOs for the XML messages being sent and received on the wire.

The point of a web services toolkit, or "stack" is to provide this for you, so that you don't have to author classes and XML serialization code by hand.

Did you try this? Did you try to run the WSDL through one of those tools? Or did you try to "Add web reference" in Visual Studio?


After updating the question, I suggest that you modify the WSDL, rather than write custom code. You can produce a custom WSDL for the service, which will correctly generate the proxy classes you want. If you don't need all 100 methods (or however many there are), then leave them out. If you want a custom object from a method, then define a complexType that corresponds to that object. This is much simpler and more reliable than hand-authoring XML deserialization code for each method.


If you don't like that idea, and want to stick with manually writin the XML deserialization code, then you need to do two things:

  1. attach a namespace to the XmlRoot attribute.

  2. change the name of your class to ResponseExt, and derive it from a class called Response. Decorate that Response class with an XmlInclude attribute. This aligns the use of the Xml Serializer with the xsi:type used in the XML fragment.

It looks like this in code:

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
public class ResponseExt : Response {
}

[XmlRoot("Response", Namespace="http://www.thirdparty.com/lr/")]
[XmlInclude(typeof(ResponseExt))]
public class Response {
public string Code {get; set;}
public string Message {get; set;}
public string SessionId {get; set;}
}

public class XsiType
{
public static void Main(string[] args)
{
try
{
string filename = "XsiType.xml";
XmlSerializer s1 = new XmlSerializer(typeof(Response));
ResponseExt r = null;
using(System.IO.StreamReader reader= System.IO.File.OpenText(filename))
{
r= (ResponseExt) s1.Deserialize(reader);
}

var builder = new System.Text.StringBuilder();
var xmlws = new System.Xml.XmlWriterSettings { OmitXmlDeclaration = true, Indent= true };
using ( var writer = System.Xml.XmlWriter.Create(builder, xmlws))
{
//s1.Serialize(writer, r, ns);
s1.Serialize(writer, r);
}
string xml = builder.ToString();
System.Console.WriteLine(xml);

}
catch (System.Exception exc1)
{
Console.WriteLine("Exception: {0}", exc1.ToString());
}
}
}

related: How can I force the use of an xsi:type attribute?

I got this error when deserializing XML in C#

There's a few issues I see here, both with the XML and with the class setup.

  • These namespaces don't seem to be handled in the code:
 <S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
<S:Body>
<ns2:loginResponse xmlns:ns2="http://webservices/">
  • Your root return object has another nested object called xml in it, so you should add an xml class in there, like this:
    [Serializable()]
[XmlRoot("return")]
public class Response
{
private Xml xml { get; set; };
}

[Serializable()]
[XmlRoot("xml")]
private class Xml
{
private string ticket { get; set; };
private string name { get; set; };
private string profile { get; set; };
private string companyId { get; set; };
private string storeId { get; set; };
private string terminalId { get; set; };
private string accountNo { get; set; };
private bool postae { get; set; };
private bool postaeproduct { get; set; };
}

Deserialize response in object C#

You are not telling the serialiser what the default namespace is.

Try this:

var stream = new StringReader(dd);
XmlSerializer s = new XmlSerializer(typeof(RunQueryReply), "http://www.ibm.com/xmlns/db2/cm/beans/1.0/schema");
var a = (RunQueryReply) s.Deserialize(stream);


Related Topics



Leave a reply



Submit