XmlSerializer: Changes from .NET4 to 4.5

As I wrote in one of my last post (XmlSerializer bug in .NET4.5 and legacy serializer) was the XmlSerializer completely redesignd and rewritten in .NET 4.5. This leads to several problems (see my last post).

Also it leads to problems if you use a newer development environmen as you have supported environments in the field. Especially if your application must support .NET4 and you use for development a later VS version (>2010). In this case you might use indirectly Features, which are not available in a pure .NET4 environment. One of the feature affects serialization of “lists”.

Here is a small example, which works perfectly in .NET4.5 and later, but fails with .NET4:

using System.Collections.Generic;
using System.Xml.Serialization;
namespace ConsoleApplication
{
    public class Program
    {
        public Program() { Persons = new List<Person>(); }
        static void Main()
        {
            var ser = new XmlSerializer(typeof(Program));
            var r = new System.IO.StringReader("<Program><Persons><Person><Name>TEST</Name></Person></Persons></Program>");
            ser.Deserialize(r);
        }
        public List<Person> Persons { get; private set; }
    }
    public class Person
    {
        public string Name { get; set; }
    }
}

Starting this with a computer having only .NET4 will lead to the following error message during generation of the XmlSerializer:

Unbehandelte Ausnahme: System.InvalidOperationException: Temporäre Klasse kann nicht generiert werden (result=1).
error CS0200: Property or indexer 'ConsoleApplication.Program.Persons' cannot be assigned to -- it is read only
error CS0200: Property or indexer 'ConsoleApplication.Program.Persons' cannot be assigned to -- it is read only

   bei System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence)
   bei System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies)
   bei System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence)
   bei System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace)
   bei System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace)
   bei ConsoleApplication.Program.Main() in ...\ConsoleApplication6\Program.cs:Line 12.

So what is the problem here in .NET4?

It seems that if there is a setter available (regardless of private or public), the serializer will try to use it… you can even test it with a newer .NET Version, by using the “legacy XmlSerializer” in your “app.config”:

<configuration>
  <appSettings> 
    <add key="System:Xml:Serialization:UseLegacySerializerGeneration" value="true" />
  </appSettings>
</configuration>

Solution: Just remove the setter from the property “Persons” and it works in .NET4 and .NET4.5 and later:

using System.Collections.Generic;
using System.Globalization;
using System.Xml.Serialization;
namespace ConsoleApplication
{
    public class Program
    {
        public Program() { Persons = new List<Person>(); }
        static void Main()
        {
            var ser = new XmlSerializer(typeof(Program));
            var r = new System.IO.StringReader("<Program><Persons><Person><Name>TEST</Name></Person></Persons></Program>");
            ser.Deserialize(r);
        }
        public List<Person> Persons { get; }
    }
    public class Person
    {
        public string Name { get; set; }
    }
}