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:
attach a namespace to the XmlRoot attribute.
change the name of your class to
ResponseExt
, and derive it from a class calledResponse
. 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 calledxml
in it, so you should add anxml
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
How to Pass Values Between Forms in C# Windows Application
Why Must We Define Both == and != in C#
Units of Measure in C# - Almost
Ascending/Descending in Linq - Can One Change the Order via Parameter
How to Get a List of Keys from JSON.Net
Entity Framework - Retrieve Id Before 'Savechanges' Inside a Transaction
How to Communicate with a Windows Service
Entity Framework: How to Disable Lazy Loading for Specific Query
Break Out of a While Loop That Contains a Switch Statement
Ms Dynamics Crm Online 2011 - Authentication Issues
Reading a Key from the Web.Config Using Configurationmanager
Expression.Lambda and Query Generation at Runtime, Simplest "Where" Example
Convert Rows from a Data Reader into Typed Results
Why Can't I Have Protected Interface Members
ASP.NET Core Appsettings.JSON Update in Code
Cast Linq Result to Observablecollection
Memory Efficiency and Performance of String.Replace .Net Framework