6

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.

hdougie
  • 344
  • 4
  • 9

2 Answers2

5

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);
      }
    }
  }
Swift
  • 1,861
  • 14
  • 17
  • Have just worked out that this will work, thanks. It would be nice if I could just define the "XmlRoot" attribute only on the abstract class as it will always be the same regardless of any implementations. Are you aware of any way to achieve this, or do I just have to keep duplicating the "XmlRoot" across any concrete classes? – hdougie Jul 19 '13 at 11:04
  • 1
    No you can't do it only in the base class, you can check the question http://stackoverflow.com/questions/2076024/is-xmlrootattribute-inheritable for the inheretance of attributes – Swift Jul 19 '13 at 11:30
1

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
emerson
  • 11
  • 2