Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic List XML Serialization with different classes

I have got the following code:

BaseContent.cs

public class BaseContent
{
   // Some auto properties
}

News.cs

public class News : BaseContent
{
   // Some more auto properties
}

Events.cs

public class Event : BaseContent
{
   // Some more auto properites
}

GenericResponse.cs

public class GenericResponse<T> 
{
  [XmlArray("Content")]
  [XmlArrayItem("NewsObject", typeof(News)]
  [XmlArrayItem("EventObject", typeof(Event)]
  public List<T> ContentItems { get; set; }
}

NewsResponse.cs

public class NewsResponse : GenericResponse<News> {}

EventResponse.cs

public class EventResponse : GenericResponse<Event> {}

As you can see, I have a base class BaseContent and two classes deriving from it. Next I have a generic response class, since the structure of the xml-files is always the same, but they differ in some properties.

I thought I can specify with the [XmlArrayItem] which name to use for a specific class. But now I get the error:

System.InvalidOperationException: Unable to generate a temporary class (result=1). error CS0012: The type 'System.Object' is defined in an assembly that is not referenced. You must add a reference to assembly 'System.Runtime, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'.

I can not add this reference, because I'm working on a Windows 8 App.

If I comment out one of the [XmlArrayItem] it is working well.

Anyone got an idea to solve this problem?

Update I can not use DataContractSerializer, because I have to use XmlAttributes

like image 760
Manuel Rauber Avatar asked Dec 23 '12 11:12

Manuel Rauber


People also ask

What is the correct way of using XML serialization?

As with the CreatePo method, you must first construct an XmlSerializer, passing the type of the class to be deserialized to the constructor. Also, a FileStream is required to read the XML document. To deserialize the objects, call the Deserialize method with the FileStream as an argument.

What is the basic difference between SOAP serialization and XML serialization?

XML serialization can also be used to serialize objects into XML streams that conform to the SOAP specification. SOAP is a protocol based on XML, designed specifically to transport procedure calls using XML. To serialize or deserialize objects, use the XmlSerializer class.

Why do we use XmlSerializer class?

XmlSerializer enables you to control how objects are encoded into XML. The XmlSerializer enables you to control how objects are encoded into XML, it has a number of constructors.

What is serializing XML?

XML serialization is the process of converting XML data from its representation in the XQuery and XPath data model, which is the hierarchical format it has in a Db2® database, to the serialized string format that it has in an application.


Video Answer


2 Answers

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
}
like image 164
Rui Jarimba Avatar answered Oct 09 '22 23:10

Rui Jarimba


I found my solution. No the best, but it is working.

I impleted IXmlSerializable and handeled the stuff by myself:

public void ReadXml(System.Xml.XmlReader reader)
{
    reader.Read();
    reader.MoveToContent();

    if (reader.LocalName == "AnotherNode")
    {
        var innerXml = Serializer<AnotherClass>.CreateSerializer();
        Remove = (AnotherClass) innerXml.Deserialize(reader);
        reader.MoveToContent();
    }

    reader.Read();

    // Here is the trick
    if (reader.IsStartElement())
    {
        do
        {
            var innerXml = Serializer<T>.CreateSerializer();

            var obj = (T) innerXml.Deserialize(reader);
            Updates.Add(obj);
        } while (reader.MoveToContent() == XmlNodeType.Element);
    }
}

public void WriteXml(System.Xml.XmlWriter writer)
{
    var removeSerializer = Serializer<RemoveElement>.CreateSerializer();
    removeSerializer.Serialize(writer, Remove);

    if (Updates.Any())
    {
        var innerXml = Serializer<T>.CreateSerializer();
        writer.WriteStartElement("ContentUpdates");
        foreach (var update in Updates)
        {
            innerXml.Serialize(writer, update);
        }
        writer.WriteEndElement();
    }
}
like image 3
Manuel Rauber Avatar answered Oct 09 '22 22:10

Manuel Rauber