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.

kick it on DotNetKicks.com


I am working for a great consultancy company called Diversify, located in Sweden. We are hiring skilled .NET developers. If you are interested, don't hesitate to get in touch with me.

Comments

January 12. 2010 03:28

Marc Gravell

Interesting - it looks like it has checked that the property has a set accessor, but not checked the accessibility. I'll have to take a nose...

Marc Gravell

January 12. 2010 03:30

Marc Gravell

BTW; did you have [XmlInclude(typeof(Manager))] in there?

Marc Gravell

January 12. 2010 03:35

Marc Gravell

I'd agree this is a bug; I've never spotted the specific scenario, but you can reproduce with a manual field/property:

    public List<Employee> Subordinates {
        get { return _subordinates; }
        private set { _subordinates = value; }
    }

Perhaps log it on "connect"?

Marc Gravell

January 12. 2010 03:39

fredrik

Yes, it definitely looks like the private setter is to blame. [XmlInclude(typeof(Manager))] seems to make no difference.

fredrik

January 13. 2010 20:05

trackback

Trackback from alcedo.com // software blog

Object construction and the DataContractSerializer

alcedo.com // software blog

February 25. 2010 19:23

trackback

Readonly collection properties and XmlSerializer

You've been kicked (a good thing) - Trackback from DotNetKicks.com

DotNetKicks.com

April 28. 2010 23:20

Isaac

Man you saved me hours of implementing custom serialization methods for every class in my project.  Absolutely agree with you, sound like a bug.

Thanx!!

Isaac

June 27. 2012 16:09

Devon Burriss

Old post but helped confirm what I suspected.

I am having this issue when running my unit test but it is not just for collections.
public string ParentsDesc { get; private set; }

throws the same error, int fields as well (I thought maybe string might be considered a collection of char or something).

Based on what the sited as "By Design" surely this does not fall into that?

Devon Burriss

June 28. 2012 12:13

fredrik

@Devon: I think it boils down to the definition of "a readonly property". From an outside perspective the property is readonly even if it has a private setter but techincally, as pointed out, the property can be assigned if there is a private setter (even from the outside, using reflection). So I can see how it is by design, but I still think it is a strange behavior.

fredrik

September 12. 2012 23:56

pingback

Pingback from jisku.com

XML Serializer not serializing property which has public getter but no setter | Jisku.com - Developers Network

jisku.com