Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Setter not called when deserializing collection

I am trying to do a very simple bit of serialization with XmlSerializer:

public struct XmlPerson
{
    [XmlAttribute] public string Id   { get; set; }
    [XmlAttribute] public string Name { get; set; }
}

public class GroupOfPeople
{
    private Dictionary<string, string> _namesById = new Dictionary<string, string>();

    //pseudo property for serialising dictionary to/from XML
    public List<XmlPerson> _XmlPeople
    {
        get
        {
            var people = new List<XmlPerson>();
            foreach (KeyValuePair<string, string> pair in _namesById )
                people.Add(new XmlPerson() { Id = pair.Key, Name = pair.Value });

            return people;
        }
        set
        {
            _namesById.Clear();
            foreach (var person in value)
                _namesById.Add(person.Id, person.Name);
        }
    }     
} 

Saving this class works fine, and I get:

<?xml version="1.0" encoding="utf-8"?>
<GroupOfPeople xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <_XmlPeople>
        <XmlPerson Id="person1" Name="Fred" />
        <XmlPerson Id="person2" Name="Bill" />
        <XmlPerson Id="person3" Name="Andy" />
        <XmlPerson Id="person4" Name="Nagesh" />
    </_XmlPeople>
</GroupOfPeople>

However, when I read in the file again, my _XmlPeople property setter is never called, and thus the dictionary is empty. All other properties on this object get deserialized fine.

Am I missing something obvious? I have tried various collection types but none of them deserialize.

EDIT: Read code:

try
{
    using (var stream = new StreamReader(itemPath))
    {
        var xml = new XmlSerializer(typeof(GroupOfPeople));
        GroupOfPeople item = (GroupOfPeople)xml.Deserialize(stream);  
    }  
}
//snip error stuff
like image 906
GazTheDestroyer Avatar asked Apr 23 '12 13:04

GazTheDestroyer


2 Answers

Answer for clarity:

Have done some debugging and found that XmlSerializer does not call the setter for a collection.

Instead is calls the getter, and then adds items to the collection returned. Thus a solution such as Felipe's is necessary.

like image 50
GazTheDestroyer Avatar answered Sep 22 '22 13:09

GazTheDestroyer


Have you tried using the XmlArray attribute?

With your example it would be something like this:

[XmlArray]
[XmlArrayItem(ElementName="XmlPerson")]
public List<XmlPerson> XmlPeople

EDIT:

Here, try the following structure:

public struct XmlPerson
{
    [XmlAttribute] public string Id   { get; set; }
    [XmlAttribute] public string Name { get; set; }
}


public class GroupOfPeople
{
    [XmlArray]
    [XmlArrayItem(ElementName="XmlPerson")]
    public List<XmlPerson> XmlPeople { get; set; }
}

I don't think it will be easy to add code to the Setter of the list, so what about getting that Dictionary when you actually need it?

Like this:

private Dictionary<string, string> _namesById;

public Dictionary<string, string> NamesById
{
    set { _namesById = value; }
    get
    {
        if (_namesById == null)
        {
            _namesById = new Dictionary<string, string>();

            foreach (var person in XmlPeople)
            {
                 _namesById.Add(person.Id, person.Name);
            }
        }

        return _namesById;
    }
}

This way you'll get the items from the XML and will also mantain that Dictionary of yours.

like image 32
Smur Avatar answered Sep 21 '22 13:09

Smur