XML Serialize / Deserialize Abstract Types

  • Thread starter Thread starter xtopher.brandt
  • Start date Start date
X

xtopher.brandt

I'm sure this problem has come up, but I haven't been able to find a
good discussion of it.

I have a type Animal which is an abstract base class. From that type I
derive Horse and Dog which are concrete classes. I include an Animal in
my BarnYardStall type. Now I want to serialize BarnYardStall to XML,
ship the XML somewhere and deserialize it back to a BarnYardStall.

The only way I can find to make this work is to add an XmlElement
attribute for each derived type of Animal to the Animal property in
BarnYardStall. The obvious problem with this approach is that I may
need to use Animal in other types, such as Stock, History,.... And I
may add new types of derived Animals such as Cat and Cow. When I add
these new Animal derivations, I have to also add an associated
XmlElement attribute to EVERY class that needs to serialize an Animal.
That's a huge problem.

My expectation is that I should be able to add the XmlInclude attribute
to the abstract Animal class. But this doesn't work, and I suspect,
based on the documentation, that it was intended for SOAP methods.

Here is the code:

public abstract class Animal
{
private String name;
public String Name
{
get{ return this.name }
set{ this.name = value }
}
}

public class Horse : Animal
{
}

public class Dog : Animal
{
}

[XmlRoot]
public class BarnYardStall
{
private Animal occupant;

[XmlElement ( ElementName="Horse", Type=typeof(Horse) )]
[XmlElement ( ElementName="Dog", Type=typeof(Dog) )]
public Animal Occupant
{
get { return this.occupant }
set { this.occupant = value }
}
}

[XmlRoot]
public class Stock
{
private Animal[] animalList;

[XmlArray( "Animals" )]
[XmlArrayItem( "Dog", typeof(Dog) )]
[XmlArrayItem( "Horse", typeof(Horse) )]
public Animal[] AnimalList
{
get { return this.animalList}
set { this.animalList= value }
}
}
 
Upon further investigation I have found that the problem is actually in
the namespaces. While the code above works, it is not desired for the
reasons I specified. The following is the correct implementation. The
problem I was having (which I didn't even replicate in my example) what
that I was declaring a namespace with an XmlRoot on only the root
element (BarnYardStall). So when the serializer tried to find the type
derived type of Animal from the namespace declared for BarnYardStall,
it couldn't. Once I added the XmlRoot with the same namespace to
Animal, then it found it and everything worked fine.

My question now is, can I apply multiple XmlRoot attributes to the same
class? For example if I have a new type (AnimalShow) that belongs to a
different namespace, but still has Animals I need to update the Animal
class to be included in that namespace.

[XmlInclude ( typeof(Dog) )]
[XmlInclude ( typeof(Horse) )]
[XmlRoot ( Namespace="uri-sample.com" )]
public abstract class Animal
{
private String name;
public String Name
{
get{ return this.name }
set{ this.name = value }
}



}


public class Horse : Animal
{


}


public class Dog : Animal
{


}


[XmlRoot ( Namespace="uri-sample.com" )]
public class BarnYardStall
{
private Animal occupant;

[XmlElement]
public Animal Occupant
{
get { return this.occupant }
set { this.occupant = value }
}



}


[XmlRoot ( Namespace="uri-sample.com" )]
public class Stock
{
private Animal[] animalList;

[XmlArray( "Animals" )]
[XmlArrayItem( "Animal", typeof(Animal) )]
public Animal[] AnimalList
{
get { return this.animalList}
set { this.animalList= value }
}
 
Back
Top