0
votes

Because XmlSerializer won't work on dictionaries, I use the XmlIgnore attribute on them, and use a mediating List<> property to render the dictionary as a list, which does work with serialization. However, my List<> property is ignored on deserialization. (There's no error thrown, and no serialization events are raised.) Why?

[XmlIgnore]
public Dictionary<string, string> ConnectionStrings { get; set; }

Here is the "surrogate" list property that is supposed to used for de/serialization. Like I say, this works on serialization, but the property is ignored during deserialization. I'm trying to understand why/what to do about it....

public List<ConnectionItem> SerializedConnections
{
    get 
    { 
        return ConnectionStrings.Select(keyPair => new ConnectionItem() { Name = keyPair.Key, ConnectionString = keyPair.Value }).ToList(); 
    }
    set 
    {
        ConnectionStrings = new Dictionary<string, string>();
        foreach (var ci in value) ConnectionStrings.Add(ci.Name, ci.ConnectionString);
    }
}

I tried setting a breakpoint in the set accessor of my SerializedConnections property, but it's never hit.

1
I don't know why Dictionary isn't serializable, it's not that difficult to do. You can make your own SerializableDictionary class and implement IXmlSerializable to serialize the data. I've done it a few times but I don't have the code handy to share. - Ron Beyer

1 Answers

1
votes

This is never going to work like you've assumed. During deserialization, XmlSerializer invokes the getter to retrieve a presumably empty List<T> instance, and then calls its Add(T item) method to fill it with deserialized objects.

The documentation in MSDN, in particular, says:

The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases. A class that implements ICollection (such as CollectionBase) in addition to IEnumerable must have a public Item indexed property (indexer in C#) that takes an integer, and it must have a public Count property of type integer. The parameter to the Add method must be the same type as is returned from the Item property, or one of that type's bases. For classes that implement ICollection, values to be serialized are retrieved from the indexed Item property, not by calling GetEnumerator.

The core issue of your design is that the SerializedConnections property behaves as a data generator. In other words, every time you invoke the getter a fresh instance is returned. Properties shouldn't work like that—only methods should.

A possible solution is to implement a custom collection implementing IList<ConnectionItem> and using a Dictionary<string, string> as backing storage. Then the ConnectionStrings property, or better a private field, would be of this custom type, and SerializedConnections property would behave correctly during serialization/deserialization.