Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to deserialize concrete implementation of abstract class from XML

I have an abstract class with a couple of concrete implementations. This needs serializing to XML in order to send to another system - this is working fine. However, I also need to be able to deserialize the same XML structure back. No matter what I try, I don't seem to be able to do this. My class structure is as below:

Abstract Class:

[XmlIncludeAttribute(typeof(ConcreteFooOne))]
[XmlIncludeAttribute(typeof(ConcreteFooTwo))]
[XmlIncludeAttribute(typeof(ConcreteFooThree))]
[XmlRoot(ElementName = "FooData", Namespace="http://foo.bar")]
public abstract partial class AbstractFoo
{
    // Some abstract props etc.
}

Concrete Class Example:

public partial class ConcreteFooOne : AbstractFoo
{
    // Some properties, constructor etc.
}

XML Root Example:

<FooData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ConcreteFooOne" RequestResponse="Request" xmlns="http://foo.bar">

Only included XML root as example as this appears to be where the issue is. Now I can serialize fine, but on the deserialization, if I deserialize by passing in the abstract type, I of course get an exception stating that type "AbstractFoo" is abstract. So I simply changed the logic so that instead the concrete type (ConcreteFooOne in this case) is passed to the serializer. Now I get a "http://foo.bar'> was not expected". I am presuming that this is because the serializer doesn't know what to root node should be?

I have the root node defined on the abstract class as this will be the same for all concrete implementations. The concrete type is defined by the "RequestResponse" attribute (or the xsi:type attribute can work too if it is present as that gives us the actual type name). Is there a way to make the serializer pick up what is required off the abstract class or am I going completely the wrong way about this?

  • Note that I can't change the class structure too much as it is based very closely off some XML schemas provided by a 3rd party.

Thanks in advance for anyone's help with this, it would be much appreciated.

like image 577
hdougie Avatar asked Jul 19 '13 10:07

hdougie


2 Answers

Add [XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")] to the sub-classes

here is an example I made:

  [XmlIncludeAttribute(typeof(ConcreteFooOne))]
  [XmlIncludeAttribute(typeof(ConcreteFooTwo))]
  [XmlIncludeAttribute(typeof(ConcreteFooThree))]
  [XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
  public abstract partial class AbstractFoo
  {
    // Some abstract props etc.
  }

  [XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
  public class ConcreteFooOne : AbstractFoo
  {
    public int MyProp { get; set; }
  }
  [XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
  public class ConcreteFooTwo : AbstractFoo
  {

  }
  [XmlRoot(ElementName = "FooData", Namespace = "http://foo.bar")]
  public class ConcreteFooThree : AbstractFoo
  {

  }

  class Program
  {
    static void Main(string[] args)
    {
      var serializer = new System.Xml.Serialization.XmlSerializer(typeof(AbstractFoo));
      using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
      {
        serializer.Serialize(stream, new ConcreteFooOne() { MyProp = 10 });
        stream.Flush();
      }


      using (var stream = new FileStream("test.txt", FileMode.OpenOrCreate))
      {
        var c = serializer.Deserialize(stream);
      }
    }
  }
like image 153
Swift Avatar answered Oct 23 '22 13:10

Swift


it's simple, in the client when you deserealize, define a XmlSerializer like:

XmlSerializer xs= new XmlSerializer (typeof (AbstractFoo),
                  new Type[] { typeof (ConcreteFooOne), typeof (ConcreteFooTwo) } );

then you can try:

//it instantiate the correct class, need a streamreader
var myclass = xs.Deserialize(reader);

if (myclass is ConcreteFooOne)
//do something

if (myclass is ConcreteFooTwo)
//do something
like image 35
emerson Avatar answered Oct 23 '22 12:10

emerson