How to deserialize concrete implementation of abstract class from XML - c#

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.

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);
}
}
}

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

Related

Serializing Interface array

I am trying to implement a way to save a set of objects to file, and then read it back to objects again.
I want to serialize the objects to XML (or JSON). The objects consists of one master object which holds an array of all the other objects. The array is of the type Interface, to allow several different types of child objects with some common functionality.
Obviously, there will be a problem during deserialization because the type of the interface object is not known.
Example:
[Serializable]
public class MasterClass
{
public ImyInterface[] subObjects;
}
public interface ImyInterface
{
}
How can I serialize/deserialize these objects?
My suggestions:
Add information about the object type in the serialized data.
Use a different solution than interface.
This is not the only way to serialize your data, but it is a ready to use solution from the framework:
DataContractSerializer supports this is you don't mind adding attributes for each of the available implementations of the interface:
[DataContract]
[KnownType(typeof(MyImpl))] // You'd have to do this for every implementation of ImyInterface
public class MasterClass
{
[DataMember]
public ImyInterface[] subObjects;
}
public interface ImyInterface
{
}
public class MyImpl : ImyInterface
{
...
}
Serializing/deserializing:
MasterClass mc = ...
using (var stream = new MemoryStream())
{
DataContractSerializer ser = new DataContractSerializer(typeof(MasterClass));
ser.WriteObject(stream, mc);
stream.Position = 0;
var deserialized = ser.ReadObject(stream);
}
For JSON you could use DataContractJsonSerializer instead.
One solution is to use an abstract class instead of an interface:
public class MasterClass
{
public MyAbstractClass[] subObjects;
}
[XmlInclude(typeof(MyImpl ))] //Include all classes that inherits from the abstract class
public abstract class MyAbstractClass
{
}
public class MyImpl : MyAbstractClass
{
...
}
It can be serialized/deserialized with the XmlSerializer:
MasterClass mc = ...
using (FileStream fs = File.Create("objects.xml"))
{
xs = new XmlSerializer(typeof(MasterClass));
xs.Serialize(fs, mc);
}
using (StreamReader file = new StreamReader("objects.xml"))
{
XmlSerializer reader = new XmlSerializer(typeof(MasterClass));
var deserialized = reader.Deserialize(file);
}

Serialization of class attributes?

Is the default XmlSerializer capable of serializing class attributes as Xml attributes?
[MyClassTypeAttribute(ClassType.MyClass)]
public MyClass : BaseClass {
}
would turn to
<myclass MyClassType="MyClass">
Reason:
I have a WCF service that sends me different objects through the same operation contract which all derive from BaseClass. To know which type of object it is and to cast it directly (and serialize it as Xml to write in a document afterwards), I'd like to have some 'type' attribute (enum).
One possibility is, of course, declaring a property as XmlAttribute
[XmlAttribute(params)]
public MyClassType { get; set; }
Problem here is: The XmlSerializer (DataContractSerializer as well, AFAIK) forces me to have a setter on every property. I know I can declare the setter as protected and it still works (XmlSerializer, you naughty little thing), but don't really like that solution because 1) I think there is a reason that I'm able to leave out the setter in POCOs usually and 2) declaring some properties as XmlAttributes and others as XmlElements is confusing (it's like putting dogs and cats into a cat goulash.
(Additionally, is it possible to force a derived class to declare certain attributes?)
[abstract MyClassTypeAttribute]
if it is about the type of your class here is an example:
[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);
}
}
}
The code will serialize and include the type attribute and when you deserialize you will get the right instance.

Use the XmlInclude or SoapInclude attribute to specify types that are not known statically

I've got a very strange problem when working with .NET's XmlSerializer.
Take the following example classes:
public class Order
{
public PaymentCollection Payments { get; set; }
//everything else is serializable (including other collections of non-abstract types)
}
public class PaymentCollection : Collection<Payment>
{
}
public abstract class Payment
{
//abstract methods
}
public class BankPayment : Payment
{
//method implementations
}
AFAIK, there are three different methods to solve the InvalidOperationException that's caused by the serializer not knowing about the derived types of Payment.
1. Adding XmlInclude to the Payment class definition:
This is not possible due to all classes being included as external references over which I have no control of.
2. Passing the derived types' types during creation of the XmlSerializer instance
Doesn't work.
3. Defining XmlAttributeOverrides for the target property in order to override the property's default serialization (as explained in this SO post)
Also doesn't work (XmlAttributeOverrides initialization follows).
Type bankPayment = typeof(BankPayment);
XmlAttributes attributes = new XmlAttributes();
attributes.XmlElements.Add(new XmlElementAttribute(bankPayment.Name, bankPayment));
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
overrides.Add(typeof(Order), "Payments", attributes);
The appropriate XmlSerializer constructor would then be used.
NOTE: by doesn't work I mean the InvalidOperationException (BankPayment was not expected...) is thrown.
Can anyone shed some light on the subject? How would one go about and debug the issue further?
This worked for me:
[XmlInclude(typeof(BankPayment))]
[Serializable]
public abstract class Payment { }
[Serializable]
public class BankPayment : Payment {}
[Serializable]
public class Payments : List<Payment>{}
XmlSerializer serializer = new XmlSerializer(typeof(Payments), new Type[]{typeof(Payment)});
Just solved the issue. After digging around for a while longer, I found this SO post which covers the exact same situation. It got me in the right track.
Basically, the XmlSerializer needs to know the default namespace if derived classes are included as extra types. The exact reason why this has to happen is still unknown but, still, serialization is working now.
Base on this I was able to solve this by changing the constructor of XmlSerializer I was using instead of changing the classes.
Instead of using something like this (suggested in the other answers):
[XmlInclude(typeof(Derived))]
public class Base {}
public class Derived : Base {}
public void Serialize()
{
TextWriter writer = new StreamWriter(SchedulePath);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>));
xmlSerializer.Serialize(writer, data);
writer.Close();
}
I did this:
public class Base {}
public class Derived : Base {}
public void Serialize()
{
TextWriter writer = new StreamWriter(SchedulePath);
XmlSerializer xmlSerializer = new XmlSerializer(typeof(List<Derived>), new[] { typeof(Derived) });
xmlSerializer.Serialize(writer, data);
writer.Close();
}
Just do it in the Base, that way any child can be Serialized, less code cleaner code.
public abstract class XmlBaseClass
{
public virtual string Serialize()
{
this.SerializeValidation();
XmlSerializerNamespaces XmlNamespaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
XmlWriterSettings XmlSettings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true
};
StringWriter StringWriter = new StringWriter();
XmlSerializer Serializer = new XmlSerializer(this.GetType());
XmlWriter XmlWriter = XmlWriter.Create(StringWriter, XmlSettings);
Serializer.Serialize(XmlWriter, this, XmlNamespaces);
StringWriter.Flush();
StringWriter.Close();
return StringWriter.ToString();
}
protected virtual void SerializeValidation() {}
}
[XmlRoot(ElementName = "MyRoot", Namespace = "MyNamespace")]
public class XmlChildClass : XmlBaseClass
{
protected override void SerializeValidation()
{
//Add custom validation logic here or anything else you need to do
}
}
This way you can call Serialize on the child class no matter the circumstance and still be able to do what you need to before object Serializes.
I agree with bizl
[XmlInclude(typeof(ParentOfTheItem))]
[Serializable]
public abstract class WarningsType{ }
also if you need to apply this included class to an object item you can do like that
[System.Xml.Serialization.XmlElementAttribute("Warnings", typeof(WarningsType))]
public object[] Items
{
get
{
return this.itemsField;
}
set
{
this.itemsField = value;
}
}
I added this [System.Xml.Serialization.XmlAttributeAttribute()] for property, its resolved for me.

How to force XML deserialization to derived types of the same name?

I have types provided in a library I cant modify, such as this:
namespace BaseNamespace
{
public class A
{
public string Foo { get; set; }
}
}
I also have a class named SomeOtherNamespace.A" that derives from BaseNamespace.A like this:
namespace SomeOtherNamespace
{
public class A : BaseNamespace.A
{
public string DoSomething() {}
}
}
From a web service I receive an XML payload with in it.
I want to deserialize the XML so that I end up with a SomeOtherNamespace.A object. However when I run the following code
string xml = "<A Foo=\"test\"></A>";
XmlSerializer serializer = new XmlSerializer(typeof(A));
StringReader reader = new StringReader(xml);
A obj = serializer.Deserialize(reader) as A;
I get an error:
Types 'BaseNamespace.A' and 'SomeOtherNamespace.A' both use the XML
type name, 'A', from namespace ''. Use XML attributes to specify a
unique XML name and/or namespace for the type.
Question: Without modification of the class BaseNamespace.A how can I force deserialization to my derived type SomeOtherNamespace.A?
Rename your SomeOtherNamespace.A as
namespace SomeOtherNamespace
{
public class AAA : BaseNamespace.A
{
public string DoSomething() {}
}
}
and create serializer as
XmlRootAttribute root = new XmlRootAttribute("A");
XmlSerializer serializer = new XmlSerializer(typeof(AAA),root);
If it is acceptable to use a different name in Xml for SomeOtherNamespace.A, you can just use XmlTypeAttribute to give A a different Xml type.
namespace SomeOtherNamespace {
[XmlType("NotA")]
public class A
{
public string Foo { get; set; }
}
}
More importantly, you may want to consider a design in which your derived class does not have the same name as the base class.

XML Serialize generic list of serializable objects with abstract base class

Any good sample on how to serialize list of generic objects with abstract base class. Samples with non abstract base class are listed in XML Serialize generic list of serializable objects. My base class is similar to Microsoft.Build.Utilities.Task
Another alternative is to use the XmlElementAttribute to move the list of known types to the generic list itself...
using System;
using System.Xml;
using System.Xml.Serialization;
using System.Collections.Generic;
public abstract class Animal
{
public int Weight { get; set; }
}
public class Cat : Animal
{
public int FurLength { get; set; }
}
public class Fish : Animal
{
public int ScalesCount { get; set; }
}
public class AnimalFarm
{
[XmlElement(typeof(Cat))]
[XmlElement(typeof(Fish))]
public List<Animal> Animals { get; set; }
public AnimalFarm()
{
Animals = new List<Animal>();
}
}
public class Program
{
public static void Main()
{
AnimalFarm animalFarm = new AnimalFarm();
animalFarm.Animals.Add(new Cat() { Weight = 4000, FurLength = 3 });
animalFarm.Animals.Add(new Fish() { Weight = 200, ScalesCount = 99 });
XmlSerializer serializer = new XmlSerializer(typeof(AnimalFarm));
serializer.Serialize(Console.Out, animalFarm);
}
}
... which will also result in a better looking XML output (without the ugly xsi:type attribute)...
<?xml version="1.0" encoding="ibm850"?>
<AnimalFarm xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Cat>
<Weight>4000</Weight>
<FurLength>3</FurLength>
</Cat>
<Fish>
<Weight>200</Weight>
<ScalesCount>99</ScalesCount>
</Fish>
</AnimalFarm>
It is often useful to have abstract classes with several derived types to allow use of strongly typed lists and the such.
For example you might have a DocumentFragment class which is abstract and two concrete classes called TextDocumentFragment and CommentDocumentFragment (this example from Willis).
This allows the creation of a List property which can contain objects only of those two types.
If you attempt to create a WebService that returns this list you get an error but this is easy to get around with the code below....
[Serializable()]
[System.Xml.Serialization.XmlInclude(typeof(TextDocumentFragment))]
[System.Xml.Serialization.XmlInclude(typeof(CommentDocumentFragment))]
public abstract class DocumentFragment {
...}
The XmlInclude attributes tell the class that it might be serialized to those two derived classes.
This generates an attribute in the DocumentFragment element specifying the actual type, as below.
<DocumentFragment xsi:type="TextDocumentFragment">
Any additonal properties specific to the derived class will also be included using this method.

Categories

Resources