locked
XmlSerializer and IList<T>

    Question

  • Has someone found any workaround for this bug? It looks like the XmlSerializer cannot serialize a member of type IList<T>.

    public class Program
    {
        public class Order { }
        public class User
        {
            private IList<Order> _orders;
            public IList<Order> Orders
            {
                get { return _orders; }
                set { _orders = value; }
            }
        }

        static void Main(string[] args)
        {
            User user = new User();
            user.Orders = new List<Order>();
            user.Orders.Add(new Order());

            XmlSerializer serializer = new XmlSerializer(typeof(User));
            serializer.Serialize(Console.Out, user);
        }
    }

    The DataContractSerializer used in WCF handles correctly members of this interface but I am using it in a classic web service which uses XmlSerializer. Declaring the member as List<T> instead of IList<T> is not a solution for me.

    Any suggestions are greatly appreciated,

    Darin
    Sunday, March 18, 2007 2:48 PM

Answers

  • Hello,

    Unfortunetly there is no good workaround for this solution.

    If you can modify the type where the IList is used, then you can define another property of type List and mark the property with IList with XmlIgnore attribute. Note that the property with List<T> will be public and the users of your type can use it with undesired behavior. Following is what you would do for this approach.

            private IList<Order> _orders;

            [XmlIgnore]
            public IList<Order> Orders
            {
                get { return _orders; }
                set { _orders = value; }
            }

            [XmlElement(Name="Orders")]
            public List<Order> _DoNotUse_Orders
            {
                get
                {
                    // copy contents of _orders to new List<Order> and return
                }
              
                set
                {
                   // copy the contents of the value to _orders
                }
           }

    Thanks,

    Vipul Modi - MSFT

     

     

    Wednesday, March 21, 2007 6:33 PM

All replies

  • Dear Friend,

          i have done XML serialization like below, may be it might help u..

    using System;
    using System.Xml.Serialization;
    using System.IO;
    using System.Text;

    namespace Zeetaa.Serialization
    {
        public class SerialDeserial
        {

            public string XMLSerializer(object pObject)
            {
                try
                {
                    XmlSerializer xs = new XmlSerializer(pObject.GetType());
                    using (StringWriter stream = new StringWriter())
                    {
                        xs.Serialize(stream, pObject);
                        stream.Flush();
                        return stream.ToString();
                    }
                }
                catch (Exception ex)
                {
                    return ex.ToString();
                }

            }


            public object XMLDeSerializer(object pObject, string strXMLString)
            {
                try
                {

                    XmlSerializer xs = new XmlSerializer(pObject.GetType());
                    using (StringReader stream = new StringReader(strXMLString))
                    {
                        return xs.Deserialize(stream);
                    }
                }
                catch (Exception ex)
                {
                    throw new InvalidOperationException("Failed to " + "create object from xml string", ex);

                }
            }
        }
    }



    How to use this??
    find the below....


    List<GenCompDet> objGenCompDet = new List<GenCompDet>();
                    GenCompDetBusLogic objGenCompDetBusLogic = new GenCompDetBusLogic();

                    objGenCompDet = objGenCompDetBusLogic.SelectAll();
                    strXMLReturnString = objGenSerial.XMLSerializer(objGenCompDet);

    in the above objGenCompDet is my object, after i serialize i store the serialized information in a string and pass the string in web service...

    How do u Deserialize that ??
    its very simple....

    List<GenCompDet> objGenCompDet = new List<GenCompDet>();
                    GenCompDetBusLogic objGenCompDetBusLogic = new GenCompDetBusLogic();
                   
                    try
                    {
                        objGenCompDet = (List<GenCompDet>)objGenSerial.XMLDeSerializer(objGenCompDet, strXMLString);
                        blnReturn = objGenCompDetBusLogic.Insert(objGenCompDet);
                    }

    in the above i pass the string which is been serialized. when de serializing u need to cast it. like what i did (List<GenCompDet>)
    Tuesday, March 20, 2007 7:56 AM
  • Hello,

    Unfortunetly there is no good workaround for this solution.

    If you can modify the type where the IList is used, then you can define another property of type List and mark the property with IList with XmlIgnore attribute. Note that the property with List<T> will be public and the users of your type can use it with undesired behavior. Following is what you would do for this approach.

            private IList<Order> _orders;

            [XmlIgnore]
            public IList<Order> Orders
            {
                get { return _orders; }
                set { _orders = value; }
            }

            [XmlElement(Name="Orders")]
            public List<Order> _DoNotUse_Orders
            {
                get
                {
                    // copy contents of _orders to new List<Order> and return
                }
              
                set
                {
                   // copy the contents of the value to _orders
                }
           }

    Thanks,

    Vipul Modi - MSFT

     

     

    Wednesday, March 21, 2007 6:33 PM
  • Of course, i suppouse that your classes Program, Order and User has the [Serializable()] attribute.

    In stead of use IList use Lit<of>, like Vipul Modi says. Try to use this code:

     

    ''' <summary>
    ''' This is a generic class that supports
    ''' serialization and deserialization for any type
    ''' </summary>
    ''' <typeparam name="T">class type</typeparam>
    ''' <remarks></remarks>
    Public Class XMLserializer(Of T)

    #Region "Serialization support"
        ''' <summary>
        ''' XML serializer
        ''' </summary>
        Private _serializer As New System.Xml.Serialization.XmlSerializer(GetType(T))

        ''' <summary>
        ''' Serialize object into XML string
        ''' </summary>
        ''' <param name="myobject"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Serialize(ByVal myobject As T) As String
            'serialise to a memory stream, then read into a string
            Try
                Dim result As String = Nothing
                If myobject IsNot Nothing Then
                    Using ms As New MemoryStream
                        Using xtw As New XmlTextWriter(ms, System.Text.Encoding.UTF8)
                            xtw.Formatting = Formatting.Indented
                            _serializer.Serialize(xtw, myobject)
                            'rewind
                            ms.Seek(0, System.IO.SeekOrigin.Begin)
                            Using reader As New StreamReader(ms, System.Text.Encoding.UTF8)
                                result = reader.ReadToEnd()
                                xtw.Close()
                                reader.Close()
                            End Using
                        End Using
                    End Using
                End If
                Return result

            Catch ex As Exception
                Throw
            End Try
        End Function

        ''' <summary>
        ''' Deserialize XML string into an instance of T
        ''' </summary>
        ''' <param name="xml"></param>
        ''' <returns></returns>
        ''' <remarks></remarks>
        Public Function Deserialize(ByVal xml As String) As T
            Try
                'default to no object
                If Not String.IsNullOrEmpty(xml) Then
                    Using sr As New StringReader(xml)
                        Return CType(_serializer.Deserialize(sr), T)
                    End Using
                Else
                    Return Nothing
                End If

            Catch ex As Exception
                Throw
            End Try
        End Function

    #End Region

    End Class


    Good Luck

    Friday, September 12, 2008 11:09 AM
  • Has there been a solution to this problem with later versions of .NET?  can one serialize an ILIST<T> without needing to change the declaration to LIST<T>?
    Friday, April 24, 2009 4:19 PM
  • No. This will never change. It will always be impossible to serialize an interface, as an interface only has behavior, and no data.

    John Saunders
    Use File->New Project to create Web Service Projects
    Use WCF for All New Web Service Development, instead of old ASMX or obsolete WSE
    Friday, May 01, 2009 11:40 AM
  • recently i came to know that microsoft has provided this provision in WCF services in xml serialization...
    Tuesday, May 05, 2009 1:11 PM
  • recently i came to know that microsoft has provided this provision in WCF services in xml serialization...

    No, they didn't.

    Perhaps you are referring to the DataContract Serializer? It does not use the same XML Serialization infrastructure.
    John Saunders
    Use File->New Project to create Web Service Projects
    Use WCF for All New Web Service Development, instead of old ASMX or obsolete WSE
    Tuesday, May 05, 2009 1:36 PM
  • Hi,

    I found a sollution to the problem but it doesn't seem to work (or maybe I'm missing the details...) http://www.dotnet247.com/247reference/msgs/16/80980.aspx
    They suggest to use an attribute on the properties that are interfaces.

    public class Contact
    {
    ....
    [XmlElement(typeof(DataItem))]
    public IDataItem HairColor //Type is IDataItem
    {
    get{return hairColor;}
    set{hairColor = value;}
    }
    ....
    }

    In my code the property looks like this :

    Private

    _uDPValues As Generic.IList(Of BLL.UDP_Value)

    <XmlElement(

    GetType(Generic.List(Of BLL.UDP_Value)))> _

    Public Overridable Property UDPValues() As Generic.IList(Of BLL.UDP_Value)

    Get

    If _uDPValues Is Nothing Then

    _uDPValues =

    New Generic.List(Of BLL.UDP_Value)

    End If

    Return _uDPValues

    End Get

    Set(ByVal value As Generic.IList(Of BLL.UDP_Value))

    _uDPValues = value

    End Set

    End Property

    But I still get the error that an interface cannot be serialized.
    We don't want to duplicate all the properties just for the sake of serialization so a sollution with an attribute that tells the serialization what datatype te use would be a great sollution.

    Is there a way to make this work or is this completely wrong?

    Thanx, Nico

    Friday, September 25, 2009 9:57 AM
  • You did not understand that article. There is no way to use XML Serialization to serialize an interface. Period. The article you referenced does not use XML Serialization to serialize an interface. Instead, it serializes properties whose type is that of an abstract base class, then lists the potential concrete derived classes using the XmlInclude attribute.

    John Saunders
    WCF is Web Services. They are not two separate things.
    Use WCF for All New Web Service Development, instead of legacy ASMX or obsolete WSE
    Use File->New Project to create Web Service Projects
    • Proposed as answer by Echtelion Friday, September 03, 2010 7:15 AM
    Friday, September 25, 2009 6:40 PM
  • Hi,

    I did the following class to serizalize data located in IList object.

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Runtime.Serialization.Formatters.Soap;
    using System.Text;

     

    internal class GenericListSerialization
        {
            private const string SEPARATOR = "<SEP>";
            public static string CreateStringFromObjects<T>( IEnumerable<T> objects)
            {
                StringBuilder sb = new StringBuilder();
                SoapFormatter formatter = new SoapFormatter();
                foreach(object o in objects)
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        formatter.Serialize(stream, o);
                        byte[] buffer = stream.ToArray();
                        if (buffer.Length > 0)
                        {
                            string xml = Encoding.UTF8.GetString(buffer);
                            sb.Append(xml + SEPARATOR);
                        }
                    }
                }
                return sb.ToString();
            }

            public IList<T> CreateObjectsFromString<T>( string rawData) where T : class
            {
                string[] objectsData = rawData.Split(new string[] {SEPARATOR}, StringSplitOptions.RemoveEmptyEntries);
                if ( objectsData.Length == 0)
                    return null;

                IList<T> result = new List<T>();
                SoapFormatter formatter = new SoapFormatter();
                foreach (string objectData in objectsData)
                {
                    using (MemoryStream stream = new MemoryStream())
                    {
                        byte[] data = Encoding.UTF8.GetBytes(objectData);
                        stream.Write(data, 0, data.Length);
                        stream.Seek(0, SeekOrigin.Begin);
                        result.Add(formatter.Deserialize(stream) as T);
                    }
                }
                return result;
            }

     

    You can change implementation to use XmlDocument instead simple StringBuilder.

     

    Cheers, Eddie.

    Monday, October 18, 2010 1:31 PM
  • Note that this does not solve the problem at all. It does not use the XmlSerializer, but instead uses the old SoapFormatter with runtime serialization.

    It is also not serializing a property of type IList<T>, but is instead serializing the elements of an IList<T>, which is not the same thing.


    John Saunders
    WCF is Web Services. They are not two separate things.
    Use WCF for All New Web Service Development, instead of legacy ASMX or obsolete WSE
    Use File->New Project to create Web Service Projects
    Monday, October 18, 2010 6:23 PM
  • No. This will never change. It will always be impossible to serialize an interface, as an interface only has behavior, and no data.

    John Saunders
    Use File->New Project to create Web Service Projects
    Use WCF for All New Web Service Development, instead of old ASMX or obsolete WSE


    Well it should be cause it makes absolute common sesne.  When you hold a reference vis-a-vis an interface, you are likely holding a reference to some concrete implementation that may store some stateful data.  Now we can get into the debate that interfaces are for enforcing behaviors and not data properties, but the fact is, in this real world - many have found partical use in using interfaces to define data members - even microsoft. I mean common, it's an ILIST, what do you think is the most likely use for it?

    The ability to annotate an interface as serializable makes good design sense imo.

    Friday, October 29, 2010 3:25 PM