Readonly collection properties and XmlSerializer

January 21, 2010

I had an issue serializing a class using the XmlSerializer, where the class had a collection property. It wasn't really a problem from the start (not with serializing anyway). The problem was that the class exposed the collection property as a read/write property, which is violating the recommendation that collection properties should be readonly (the Manager/Employee sample here is merely for illustration purposes):

public class Employee
{
    public string Name { get; set; }
}

public class Manager : Employee
{
    public Manager()
    {
        Subordinates = new List<Employee>();
    }
    public List<Employee> Subordinates { get; set; }
}

So I did some refactoring, making the property readonly (which led to some minor updates of calling code that assigned a new collection to the property). The updated Manager class looked like this;

public class Manager : Employee
{
    public Manager()
    {
        Subordinates = new List<Employee>();
    }
    public List<Employee> Subordinates { get; private set; }
}

So far all was good. Then I ran the unit tests, and got an InvalidOperationException when an XmlSerializer attempted to serialize the object:

Unable to generate a temporary class (result=1). error CS0200: Property or indexer '[namespace].Manager.Subordinates' cannot be assigned to -- it is read only

What? Does the XmlSerializer class not support the recommended way of exposing collection properties? So we move on to do some reading and find this in the document describing the FxCop rule Collection properties should be read only:

Both binary and XML serialization support read-only properties that are collections. The System.Xml.Serialization.XmlSerializer class has specific requirements for types that implement ICollection and System.Collections.IEnumerable in order to be serializable.

Here is specifically states that it should be OK to expose the collection as a read-only property and still have the XmlSerializer serialize it. So how do I get this to work? For some reason I decided to move away from the auto property approach and try how it would behave then:

public class Manager : Employee
{
    private List<Employee> _subordinates;
    
    public Manager()
    {
        _subordinates = new List<Employee>();
    }
    
    public List<Employee> Subordinates
    {
        get
        {
            return _subordinates;
        }
    }
}

And suddenly it worked. I found this behaviour slightly odd, especially since XmlSerializer does not serialize private fields, so I don't quite see how it would make a difference that the collection property has a manually created backing field, or a private set accessor.

Update: As Marc points out in the comments below; the XmlSerializer also fails if you add a private setter for the property, so this is not an issue with auto-properties as such, but you can’t resolve it using an auto-property.

Update 2: I decided to file this as a bug. Don't hesitate to vote for it if this may pose a problem for you as well.