Xml Serialize Generic List of Serializable Objects

XML Serialize generic list of serializable objects

I have an solution for a generic List<> with dynamic binded items.

class PersonalList it's the root element

[XmlRoot("PersonenListe")]
[XmlInclude(typeof(Person))] // include type class Person
public class PersonalList
{
[XmlArray("PersonenArray")]
[XmlArrayItem("PersonObjekt")]
public List<Person> Persons = new List<Person>();

[XmlElement("Listname")]
public string Listname { get; set; }

// Konstruktoren
public PersonalList() { }

public PersonalList(string name)
{
this.Listname = name;
}

public void AddPerson(Person person)
{
Persons.Add(person);
}
}

class Person it's an single list element

[XmlType("Person")] // define Type
[XmlInclude(typeof(SpecialPerson)), XmlInclude(typeof(SuperPerson))]
// include type class SpecialPerson and class SuperPerson
public class Person
{
[XmlAttribute("PersID", DataType = "string")]
public string ID { get; set; }

[XmlElement("Name")]
public string Name { get; set; }

[XmlElement("City")]
public string City { get; set; }

[XmlElement("Age")]
public int Age { get; set; }

// Konstruktoren
public Person() { }

public Person(string name, string city, int age, string id)
{
this.Name = name;
this.City = city;
this.Age = age;
this.ID = id;
}
}

class SpecialPerson inherits Person

[XmlType("SpecialPerson")] // define Type
public class SpecialPerson : Person
{
[XmlElement("SpecialInterests")]
public string Interests { get; set; }

public SpecialPerson() { }

public SpecialPerson(string name, string city, int age, string id, string interests)
{
this.Name = name;
this.City = city;
this.Age = age;
this.ID = id;
this.Interests = interests;
}
}

class SuperPerson inherits Person

[XmlType("SuperPerson")] // define Type
public class SuperPerson : Person
{
[XmlArray("Skills")]
[XmlArrayItem("Skill")]
public List<String> Skills { get; set; }

[XmlElement("Alias")]
public string Alias { get; set; }

public SuperPerson()
{
Skills = new List<String>();
}

public SuperPerson(string name, string city, int age, string id, string[] skills, string alias)
{
Skills = new List<String>();

this.Name = name;
this.City = city;
this.Age = age;
this.ID = id;
foreach (string item in skills)
{
this.Skills.Add(item);
}
this.Alias = alias;
}
}

and the main test Source

static void Main(string[] args)
{
PersonalList personen = new PersonalList();
personen.Listname = "Friends";

// normal person
Person normPerson = new Person();
normPerson.ID = "0";
normPerson.Name = "Max Man";
normPerson.City = "Capitol City";
normPerson.Age = 33;

// special person
SpecialPerson specPerson = new SpecialPerson();
specPerson.ID = "1";
specPerson.Name = "Albert Einstein";
specPerson.City = "Ulm";
specPerson.Age = 36;
specPerson.Interests = "Physics";

// super person
SuperPerson supPerson = new SuperPerson();
supPerson.ID = "2";
supPerson.Name = "Superman";
supPerson.Alias = "Clark Kent";
supPerson.City = "Metropolis";
supPerson.Age = int.MaxValue;
supPerson.Skills.Add("fly");
supPerson.Skills.Add("strong");

// Add Persons
personen.AddPerson(normPerson);
personen.AddPerson(specPerson);
personen.AddPerson(supPerson);

// Serialize
Type[] personTypes = { typeof(Person), typeof(SpecialPerson), typeof(SuperPerson) };
XmlSerializer serializer = new XmlSerializer(typeof(PersonalList), personTypes);
FileStream fs = new FileStream("Personenliste.xml", FileMode.Create);
serializer.Serialize(fs, personen);
fs.Close();
personen = null;

// Deserialize
fs = new FileStream("Personenliste.xml", FileMode.Open);
personen = (PersonalList)serializer.Deserialize(fs);
serializer.Serialize(Console.Out, personen);
Console.ReadLine();
}

Important is the definition and includes of the diffrent types.

XML Serialize generic list of serializable objects with abstract base class

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.

Generic List XML Serialization with different classes

Edit: Feel free to download the demo project

You didn't provide all the properties for the objects, so allow me to add some - just as an example:

public class BaseContent
{
[XmlAttribute("Name")]
public string Name { get; set; }
}

[XmlType(TypeName = "EventObject")]
public class Event : BaseContent
{
[XmlAttribute("EventId")]
public int EventId { get; set; }
}

[XmlType(TypeName = "NewsObject")]
public class News : BaseContent
{
[XmlAttribute("NewsId")]
public int NewsId { get; set; }
}

GenericResponse.cs could be defined this way - no need to specify the typeof for the array items:

public class GenericResponse<T>
{
[XmlArray("Content")]
public List<T> ContentItems { get; set; }

public GenericResponse()
{
this.ContentItems = new List<T>();
}
}

And then you have the response classes:

public class EventResponse : GenericResponse<Event>
{
}

public class NewsResponse : GenericResponse<News>
{
}

Example 1: Serialize an EventResponse object

var response = new EventResponse
{
ContentItems = new List<Event>
{
new Event {
EventId = 1,
Name = "Event 1"
},

new Event {
EventId = 2,
Name = "Event 2"
}
}
};

string xml = XmlSerializer<EventResponse>.Serialize(response);

Output XML:

<?xml version="1.0" encoding="utf-8"?>
<EventResponse xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<Content>
<EventObject Name="Event 1" EventId="1" />
<EventObject Name="Event 2" EventId="2" />
</Content>
</EventResponse>

If you try the same with NewsResponse it will work fine. BTW I'm using my generic XmlSerializer, click on the link to know more about it.

XmlSerializer.cs:

/// <summary>
/// XML serializer helper class. Serializes and deserializes objects from/to XML
/// </summary>
/// <typeparam name="T">The type of the object to serialize/deserialize.
/// Must have a parameterless constructor and implement <see cref="Serializable"/></typeparam>
public class XmlSerializer<T> where T: class, new()
{
/// <summary>
/// Deserializes a XML string into an object
/// Default encoding: <c>UTF8</c>
/// </summary>
/// <param name="xml">The XML string to deserialize</param>
/// <returns>An object of type <c>T</c></returns>
public static T Deserialize(string xml)
{
return Deserialize(xml, Encoding.UTF8, null);
}

/// <summary>
/// Deserializes a XML string into an object
/// Default encoding: <c>UTF8</c>
/// </summary>
/// <param name="xml">The XML string to deserialize</param>
/// <param name="encoding">The encoding</param>
/// <returns>An object of type <c>T</c></returns>
public static T Deserialize(string xml, Encoding encoding)
{
return Deserialize(xml, encoding, null);
}

/// <summary>
/// Deserializes a XML string into an object
/// </summary>
/// <param name="xml">The XML string to deserialize</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlReaderSettings"/></param>
/// <returns>An object of type <c>T</c></returns>
public static T Deserialize(string xml, XmlReaderSettings settings)
{
return Deserialize(xml, Encoding.UTF8, settings);
}

/// <summary>
/// Deserializes a XML string into an object
/// </summary>
/// <param name="xml">The XML string to deserialize</param>
/// <param name="encoding">The encoding</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlReaderSettings"/></param>
/// <returns>An object of type <c>T</c></returns>
public static T Deserialize(string xml, Encoding encoding, XmlReaderSettings settings)
{
if (string.IsNullOrEmpty(xml))
throw new ArgumentException("XML cannot be null or empty", "xml");

XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

using (MemoryStream memoryStream = new MemoryStream(encoding.GetBytes(xml)))
{
using (XmlReader xmlReader = XmlReader.Create(memoryStream, settings))
{
return (T) xmlSerializer.Deserialize(xmlReader);
}
}
}

/// <summary>
/// Deserializes a XML file.
/// </summary>
/// <param name="filename">The filename of the XML file to deserialize</param>
/// <returns>An object of type <c>T</c></returns>
public static T DeserializeFromFile(string filename)
{
return DeserializeFromFile(filename, new XmlReaderSettings());
}

/// <summary>
/// Deserializes a XML file.
/// </summary>
/// <param name="filename">The filename of the XML file to deserialize</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlReaderSettings"/></param>
/// <returns>An object of type <c>T</c></returns>
public static T DeserializeFromFile(string filename, XmlReaderSettings settings)
{
if (string.IsNullOrEmpty(filename))
throw new ArgumentException("filename", "XML filename cannot be null or empty");

if (! File.Exists(filename))
throw new FileNotFoundException("Cannot find XML file to deserialize", filename);

// Create the stream writer with the specified encoding
using (XmlReader reader = XmlReader.Create(filename, settings))
{
System.Xml.Serialization.XmlSerializer xmlSerializer = new System.Xml.Serialization.XmlSerializer(typeof(T));
return (T) xmlSerializer.Deserialize(reader);
}
}

/// <summary>
/// Serialize an object
/// </summary>
/// <param name="source">The object to serialize</param>
/// <returns>A XML string that represents the object to be serialized</returns>
public static string Serialize(T source)
{
// indented XML by default
return Serialize(source, null, GetIndentedSettings());
}

/// <summary>
/// Serialize an object
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="namespaces">Namespaces to include in serialization</param>
/// <returns>A XML string that represents the object to be serialized</returns>
public static string Serialize(T source, XmlSerializerNamespaces namespaces)
{
// indented XML by default
return Serialize(source, namespaces, GetIndentedSettings());
}

/// <summary>
/// Serialize an object
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param>
/// <returns>A XML string that represents the object to be serialized</returns>
public static string Serialize(T source, XmlWriterSettings settings)
{
return Serialize(source, null, settings);
}

/// <summary>
/// Serialize an object
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="namespaces">Namespaces to include in serialization</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param>
/// <returns>A XML string that represents the object to be serialized</returns>
public static string Serialize(T source, XmlSerializerNamespaces namespaces, XmlWriterSettings settings)
{
if (source == null)
throw new ArgumentNullException("source", "Object to serialize cannot be null");

string xml = null;
XmlSerializer serializer = new XmlSerializer(source.GetType());

using (MemoryStream memoryStream = new MemoryStream())
{
using (XmlWriter xmlWriter = XmlWriter.Create(memoryStream, settings))
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T));
x.Serialize(xmlWriter, source, namespaces);

memoryStream.Position = 0; // rewind the stream before reading back.
using (StreamReader sr = new StreamReader(memoryStream))
{
xml = sr.ReadToEnd();
}
}
}

return xml;
}

/// <summary>
/// Serialize an object to a XML file
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="filename">The file to generate</param>
public static void SerializeToFile(T source, string filename)
{
// indented XML by default
SerializeToFile(source, filename, null, GetIndentedSettings());
}

/// <summary>
/// Serialize an object to a XML file
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="filename">The file to generate</param>
/// <param name="namespaces">Namespaces to include in serialization</param>
public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces)
{
// indented XML by default
SerializeToFile(source, filename, namespaces, GetIndentedSettings());
}

/// <summary>
/// Serialize an object to a XML file
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="filename">The file to generate</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param>
public static void SerializeToFile(T source, string filename, XmlWriterSettings settings)
{
SerializeToFile(source, filename, null, settings);
}

/// <summary>
/// Serialize an object to a XML file
/// </summary>
/// <param name="source">The object to serialize</param>
/// <param name="filename">The file to generate</param>
/// <param name="namespaces">Namespaces to include in serialization</param>
/// <param name="settings">XML serialization settings. <see cref="System.Xml.XmlWriterSettings"/></param>
public static void SerializeToFile(T source, string filename, XmlSerializerNamespaces namespaces, XmlWriterSettings settings)
{
if (source == null)
throw new ArgumentNullException("source", "Object to serialize cannot be null");

XmlSerializer serializer = new XmlSerializer(source.GetType());

using (XmlWriter xmlWriter = XmlWriter.Create(filename, settings))
{
System.Xml.Serialization.XmlSerializer x = new System.Xml.Serialization.XmlSerializer(typeof(T));
x.Serialize(xmlWriter, source, namespaces);
}
}

#region Private methods

private static XmlWriterSettings GetIndentedSettings()
{
XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
xmlWriterSettings.Indent = true;
xmlWriterSettings.IndentChars = "\t";

return xmlWriterSettings;
}

#endregion
}

C# Serialize an object with a list of objects in it

Yes, using the XmlSerializer it will serialize a List<T> so long as T (or in your case Tag) is serializable.

Move move = new Move { MoveName = "MyName" };
move.oTags.Add(new Tag { TagName = "Value1" } );
move.oTags.Add(new Tag { TagName = "Value2" } );
move.oTags.Add(new Tag { TagName = "Value3" } );

StringBuilder output = new StringBuilder();
var writer = new StringWriter(output);

XmlSerializer serializer = new XmlSerializer(typeof(Move));
serializer.Serialize(writer, move);

Console.WriteLine(output.ToString());

This outputs using your current class structure as:

<?xml version="1.0" encoding="utf-16"?>
<Move xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<oTags>
<Tag>
<TagName>Value1</TagName>
</Tag>
<Tag>
<TagName>Value2</TagName>
</Tag>
<Tag>
<TagName>Value3</TagName>
</Tag>
</oTags>
<MoveName>MyName</MoveName>
</Move>

I'll see if I can find a way to match your current XML schema, but you can look up how to apply XmlAttributes and play around with it yourself.


EDIT:

If you change your class declaration to use the following XmlAttributes, you will achieve the exact XML schema as in your example:

public class Move 
{
[XmlElement(Order = 1)]
public string MoveName {get; set;}

[XmlElement(Order = 2, ElementName = "Tags")]
public List<Tag> oTags = new List<Tag>();
}

public class Tag
{
[XmlText]
public string TagName {get; set;}
}

Which when serialized will produce:

<?xml version="1.0" encoding="utf-16"?>
<Move xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MoveName>MyName</MoveName>
<Tags>Value1</Tags>
<Tags>Value2</Tags>
<Tags>Value3</Tags>
</Move>

xml serialize a generic class with it's generic types

Change your class as

public class Pair<T, U>
{
[XmlAttribute("key")]
public T key;
[XmlAttribute("value")]
public U value;
[XmlAttribute("T-Type")]
public string ttype;
[XmlAttribute("U-Type")]
public string utype;

public Pair()
{
}
public Pair(T t, U u)
{
key = t;
value = u;
ttype = typeof(T).ToString();
utype = typeof(U).ToString();
}
}

and it should work. You can not serialize/deserialize Type with Xmlserializer.(For example, suppose T is a complex object defined in an external assembly and this assembly does not exist on the computer where you want to deserialize)

C# serialize generic list customObject to file

If you want to use BinaryFormatter (which I really don't advise), you can use:

[Serializable]
class PicInfo
{
public string fileName { get; set; }
public string completeFileName { get; set; }
public string filePath { get; set; }
public byte[] hashValue { get; set; }

public PicInfo() { }
}
static class Program
{
static void Main()
{
List<PicInfo> pi = new List<PicInfo>();
pi.Add(new PicInfo {fileName = "foo.bar", hashValue = new byte[] {1, 2, 3}});

var ser = new BinaryFormatter();
using (var ms = new MemoryStream())
{
ser.Serialize(ms, pi);
var bytes = ms.ToArray();
}
}
}

If you want to use XmlSerializer (probably preferable IMO), but need the byte[], then:

public class PicInfo
{
public string fileName { get; set; }
public string completeFileName { get; set; }
public string filePath { get; set; }
public byte[] hashValue { get; set; }

public PicInfo() { }
}
static class Program
{
static void Main()
{
List<PicInfo> pi = new List<PicInfo>();
pi.Add(new PicInfo {fileName = "foo.bar", hashValue = new byte[] {1, 2, 3}});

var ser = new XmlSerializer(typeof(List<PicInfo>));
using (var ms = new MemoryStream())
{
ser.Serialize(ms, pi);
var bytes = ms.ToArray();
}
}
}

Personally, I'd use protobuf-net:

[ProtoContract]
public class PicInfo
{
[ProtoMember(1)]public string fileName { get; set; }
[ProtoMember(2)]public string completeFileName { get; set; }
[ProtoMember(3)]public string filePath { get; set; }
[ProtoMember(4)]public byte[] hashValue { get; set; }

public PicInfo() { }
}
static class Program
{
static void Main()
{
List<PicInfo> pi = new List<PicInfo>();
pi.Add(new PicInfo {fileName = "foo.bar", hashValue = new byte[] {1, 2, 3}});

using (var ms = new MemoryStream())
{
Serializer.Serialize(ms, pi);
var bytes = ms.ToArray();
}
}
}

Sizes:

  • BinaryFormatter: 488 bytes
  • XmlSerializer: 251 bytes
  • protobuf-net: 16 bytes

XMLserialize object of list and string

In order for XmlSerializer to serialize or deserialize the property Ingredienser of your Recept class, it must have a public setter as well as a public getter:

    public ListHanterare<string> Ingredienser
{
get { return ingredienser; }
set { ingredienser = value; }
}

If you don't do this, XmlSerializer will ignore the property.

You might still have problems with the class ListHanterare, but it's not shown in your question.

DataContractSerializer does not have this requirement. However, to serialize and deserialize your classes with DataContractSerializer and include private fields or properties, you would need to fully annotate them with data contract attributes. Private field and properties marked with [DataMember] get serialized despite being private. Data contract serialization is opt-in, however, so to use this feature your classes would need to be fully attributed.

Update

You don't show the complete code of your ListHanterare<string> class. In order for XmlSerializer to serialize and deserialize it successfully, it must either:

  1. Make all its contents available in publicly settable and gettable properties, or
  2. Implement ICollection<T> (or even IList<T>) as described here: XmlSerializer Class.

For method #1, you could modify your class as follows:

public class ListHanterare<T> 
{
private List<T> lista; // This is the list
private int count;

public ListHanterare()
{
lista = new List<T>();
}

[XmlElement("Item")]
public T[] SerializableList
{
get
{
return (lista == null ? new T [0] : lista.ToArray());
}
set
{
if (lista == null)
lista = new List<T>();
lista.Clear();
lista.AddRange(value ?? Enumerable.Empty<T>());
count = lista.Count;
}
}
}

You will now see the items serialized your XML.



Related Topics



Leave a reply



Submit