Deserialize to a base type

  • Thread starter Thread starter nick.fletcher
  • Start date Start date
N

nick.fletcher

Ive got a comms channel (its actually a WCF channel between a desktop
amd a pocketPc) which is using standard SOAP12 over HTTP to
communicate.

The messages represent data transfer objects and Im extanding these
using partial classes on the client side to add in the business logic.

This is all prety simple and works fine. What Im having a problem with
is the deserialisation. The message I want to use is sent as raw XML
enclosed within the WCF message and so I deserialise it manually. The
problem is that in order to do this I need to know the type which I
want to deserialise too - which makes for quite clunky procedural code

What I would like to do is deserialize my XML to a base class and then
invoke an abstract method on this. The problem occurs when i try to
deserialize to the base type and I get a "There is an error in XML
document (1,2)" error. Ive tried adding the [XMLRoot("MyBaseType")]
attribute to the derived classes but this has no effect

Any ideas?
 
Bacause deserialization means object creation, you can only deserialize
to concrete types. For BinaryFormatter this is automatic, since the
byte-stream includes the assembly-qualified class details. For xml, it
is going to believe what you say in the ctor to XmlSerializer.

IIRC, there are a few changes to BinaryFormatter behaviour between 1.1
and 2.0, specifically 2.0 can ignore fields it doesn't know about, and
can ignore fields it /does/ know about that are missing (as long as
marked optional) - however I don't know how this applies to
XmlSerializer. Certainly, however, unexpected elements/attributes can
be handled via the [XmlAnyAttribute] and [XmlAnyElement] attributes
(IIRC).
I am, however, pretty sure you won't be able to deserialize to an
abstract type (which it must be if it has an abstract method), as one
of the first requirements for XmlSerializer is "must have a public
parameterless ctor". Abstract types can't have *any* public ctor.

Perhaps look into the [XmlAnyAttribute] and [XmlAnyElement] usage
(MSDN2) and see if they make the deserializer happy (used against a
*concrete* base class).

Perhaps a second option here would be to send (separately, but perhaps
mangled into the same xml block) the details of the concrete class you
want to use (either the FullName or assembly-qualified name); obtain
this, and use reflection etc to get a Type instance to pass to
XmlSerializer?

Marc
 
Ignore my last reply; my brain was starved of caffeine.

See below. Two options, depending on whether the concrete types are
known to the base type. If they are, use [XmlInclude]; if not, just
tell the XmlSerialixer about them via the Type[].

Marc

using System;
using System.Xml.Serialization;
using System.IO;

namespace ConsoleApplication3
{
class Program
{
static void Main()
{
Test2 input = new Test2();
input.Value = 16;
input.Value2 = 3;

Test output;
// option 1: concrete types not known by base class
XmlSerializer ser = new XmlSerializer(typeof(Test), new
Type[] { typeof(Test2) });
//XmlSerializer ser = new XmlSerializer(typeof(Test));
using (MemoryStream ms = new MemoryStream())
using (StreamReader reader = new StreamReader(ms))
{
ser.Serialize(ms, input);
ms.Position = 0;
Console.WriteLine(reader.ReadToEnd()); // what xml?
ms.Position = 0;
output = (Test) ser.Deserialize(ms);
}
Console.WriteLine(output.GetType().FullName);
Console.WriteLine(output.Sum);
}
}
[Serializable]
public class Test2 : Test
{
private int value2;
public int Value2 { get { return this.value2; } set {
this.value2 = value; } }
public override int Sum { get { return Value + Value2; } }
}

// option 2: concrete types all known by base class
// [XmlInclude(typeof(Test2))]
[Serializable]
public abstract class Test
{
protected Test() { }
private int value;
//[XmlIgnore] // din't attempt to serialize Sum
public virtual int Sum { get { return Value; } }
public int Value { get { return this.value; } set { this.value
= value; } }
}
}
 
Thanks Marc

That does exactly what I want. I have a pretty much identical solution
but it wasn't working. No doubt when I check tomorrow I see my glaring
error :-)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Back
Top