I have a C# .NET 3.5 application where I would like to serialize a class containing a List<>
to XML. My class looks like this:
[XmlRoot("Foo")]
class Foo
{
private List<Bar> bar_ = new List<Bar>();
private string something_ = "My String";
[XmlElement("Something")]
public string Something { get { return something_; } }
[XmlElement("Bar")]
public ICollection<Bar> Bars
{
get { return bar_; }
}
}
If I populate it like this:
Bar b1 = new Bar();
// populate b1 with interesting data
Bar b2 = new Bar();
// populate b2 with interesting data
Foo f = new Foo();
f.Bars.Add(b1);
f.Bars.Add(b2);
And then serialize it like this:
using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(@"C:\foo.xml"))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
serializer.Serialize(textWriter, f);
}
I get a file that looks like this:
<Foo>
<Something>My String</Something>
</Foo>
But, what I want is XML that looks like this:
<Foo>
<Something>My String</Something>
<Bar>
<!-- Data from first Bar -->
</Bar>
<Bar>
<!-- Data from second Bar -->
</Bar>
</Foo>
What do I need to do to get the List<>
to appear in the XML?
The XmlSerializer
requires that serializable properties have a setter. Besides that, the XmlSerializer
can not serialize interface properties. The following code will work:
[XmlElement("Bar")]
public List<Bar> Bars
{
get { return bar_; }
set { throw new NotSupportedException("This property 'Bars' cannot be set. This property is readonly."); }
}
If you don't like this solution (the exception is kinda ugly) then you could implement IXmlSerializable
and write your own custom serialization.
Edit: Artur Mustafin is right, members that implement IEnumerable
or ICollection
don't need a setter, as is explained on this msdn page:
The XmlSerializer gives special treatment to classes that implement
IEnumerable
orICollection
. A class that implementsIEnumerable
must implement a publicAdd
method that takes a single parameter. The Add method's parameter must be of the same type as is returned from theCurrent
property on the value returned fromGetEnumerator
, or one of that type's bases. A class that implementsICollection
(such as CollectionBase) in addition toIEnumerable
must have a publicItem
indexed property (indexer in C#) that takes an integer, and it must have a publicCount
property of type integer. The parameter to theAdd
method must be the same type as is returned from theItem
property, or one of that type's bases. For classes that implementICollection
, values to be serialized are retrieved from the indexedItem
property, not by callingGetEnumerator
.
Giving a correct answer, there is no point to create ugly setter to the List<T>
public property, to throw an exception.
This is because List<>
is already implements ICollection<T>
and provides method with signature void Add(T object)
which is used by Serialization mechanism;
You are only have to add the setter to the public properties being serialized, and change ICollection<T>
to List<T>
:
[XmlRoot("Foo")]
public class Foo
{
private List<Bar> bar_ = new List<Bar>();
[XmlElement("Something")]
public string Something { get; set; }
[XmlElement("Bar")]
public List<Bar> Bars { get { return bar_; } }
}
You will get an output:
<?xml version="1.0" encoding="utf-8"?>
<Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Something>My String</Something>
<Bar />
<Bar />
</Foo>
Also, it is a better idea to serialize xml in-memory, to see the results, or test it, as follows:
static void Main(string[] args)
{
Bar b1 = new Bar();
// populate b1 with interesting data
Bar b2 = new Bar();
// populate b2 with interesting data
Foo f = new Foo();
f.Bars.Add(b1);
f.Bars.Add(b2);
f.Something = "My String";
using (MemoryStream ms = new MemoryStream())
using (System.IO.TextWriter textWriter = new System.IO.StreamWriter(ms))
{
System.Xml.Serialization.XmlSerializer serializer = new System.Xml.Serialization.XmlSerializer(typeof(Foo));
serializer.Serialize(textWriter, f);
string text = Encoding.UTF8.GetString(ms.ToArray());
Console.WriteLine(text);
}
Console.ReadKey(false);
}
To serialize using interfaces, use my project XmlSerialization
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With