Serializing a polymorphic list with the same type name

I have an object that I am trying to convert to XML. Inside this object there is a list of a general type (abstract class). Each element in this list may be a different class, but everyone inherits from an abstract base class:

public abstract class animal { public string type { get; set; } } public class cat:animal { public string size { get; set; } public string furColor { get; set; } } public class fish:animal { public string size { get; set; } public string scaleColor { get; set; } } 

When I serialize the list, I want it to look like this:

 <animal type="cat"> <size>medium</size> <furColor>black</furColor> </animal> <animal type="fish"> <size>small</size> <scaleColor>silver</scaleColor> </animal> 

I tried a simple solution:

 [XmlElement("Animal")] public List<animal> Animals { get; set; } 

But it throws an error because it does not expect the object type to be "cat". Adding the [XmlInclude] tag to a base class, a derived class, or an entire contained class (let it be called) does not help.

I can use typeof notation for one class:

 [XmlElement("Animal", typeof(cat))] public List<animal> Animals { get; set; } 

and it works correctly as I want, as long as I only use cats. Again, the moment I add the fish to the mix, it explodes with the same error (not expecting the fish).

I can add some typeof attributes:

 [XmlElement("Animal")] [XmlElementAttribute(typeof(cat))] [XmlElementAttribute(typeof(fish))] public List<animal> Animals { get; set; } 

and this compiles, but ignores the element name and serializes the objects as <cat> </cat> and <fish> </fish> , respectively, which is unacceptable.

I even tried to add some [XmlElement] tags:

 [XmlElement("Animal", typeof(cat))] [XmlElement("Animal", typeof(fish))] public List<animal> Animals { get; set; } 

In this case, another exception occurs, this time the objects "cat" and "fish" use the type "Animal" in the same area.

Can anyone think of this?

UPDATE After a bit more digging, I found This SO post , which suggests adding a namespace to the base class:

 [XmlRoot(Namespace="myNamespace")] [XmlInclude(typeof(cat))] [XmlInclude(typeof(fish))] public abstract class animal 

Serializing this gives the following:

 <animal xsi:type="cat" type="cat"> ... </animal> <animal xsi:type="fish" type="fish"> ... </animal> 

Where xsi: type = "cat" refers to the class name, and type = "cat" refers to the type attribute created in the base class (see the truest example). This is so close to what I need, and I'm afraid that I'm just suffering from inexperience here, but is there any way to get rid of the xsi: type attribute list?

+7
source share
3 answers

Adding an XmlInclude attribute to your base class should solve this serialization issue

  [Serializable] [XmlInclude(typeof(cat))] [XmlInclude(typeof(fish))] class animals { .... } public class cat:animals { public string size { get; set; } public string furColor { get; set; } } public class fish:animals { public string size { get; set; } public string scaleColor { get; set; } } 

UPDATE

Here is a short example that works on my side, in my opinion, in fact, you do not need the type attribute, because you can get it using the typeof () method. if the type attribute had no other purpose.

  List<animal> animals = new List<animal>(); cat catofdoom = new cat(); catofdoom.furColor = "brown"; catofdoom.size = "10 pounds"; animals.Add(catofdoom); fish fishofdoom = new fish(); fishofdoom.scaleColor = "blue"; fishofdoom.size = "12 inches"; animals.Add(fishofdoom); try { XmlSerializer xs = new XmlSerializer(typeof(List<animal>)); using (StreamWriter wr = new StreamWriter("animal.xml")) { xs.Serialize(wr, animals); } } catch (Exception e) { throw; } 

leads to this (very simple serialization without options):

 <?xml version="1.0" encoding="utf-8"?> <ArrayOfAnimal xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <animal xsi:type="cat"> <size>10 pounds</size> <furColor>brown</furColor> </animal> <animal xsi:type="fish"> <size>12 inches</size> <scaleColor>blue</scaleColor> </animal> </ArrayOfAnimal> 
+3
source

I suggest using a DataContractSerializer and including known types in a serializer. Using XmlInclude is a kind of gap for the encasulation principle. See the provided MSDN link for some samples ...

+1
source

You need both!

 [Serializable] [XmlInclude(typeof(cat))] [XmlInclude(typeof(fish))] class animals { .... } public class cat:animals { public string size { get; set; } public string furColor { get; set; } } public class fish:animals { public string size { get; set; } public string scaleColor { get; set; } } 

Note the slight change in the name of the XmlElement . This will work correctly.

 [XmlElement("cat", typeof(cat))] [XmlElement("fish", typeof(fish))] public List<animal> Animals { get; set; } 
+1
source

All Articles