Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XmlSerializer.Deserialize() isn't deserializing a List<T> correctly

I am new to the C# XmlSerializer so I might be missing something basic here.

The problem I am running into is that I have one class that has a List<T> of another class. When I serialize the main class the XML looks beautiful and all the data is intact. When I deserialize the XML, the data in the List<T> disappears and I am left with an empty List<T>. I am not receiving any errors and the serialization portion works like charm.

What am I missing with the deserialization process?

EDIT: Note that the code shown below does not reproduce the problem - it works. This was a simplified version of the real code, which did not work. Unfortunately, the code below was simplified enough to not reproduce the problem!

public class User
{
  public User()
  {
    this.Characters = new List<Character>();
  }
  public string Username { get; set; }
  public List<Character> Characters { get; set; }
}

public class Character
{
  public Character()
  {
    this.Skills = new List<Skill>();
  }
  public string Name { get; set; }
  public List<Skill> Skills { get; set; }
}

public enum Skill
{
  TreeClimber,
  ForkliftOperator
}

public static void Save(User user)
{
    using (var textWriter = new StreamWriter("data.xml"))
    {
        var xmlSerializer = new XmlSerializer(typeof(User));
        xmlSerializer.Serialize(textWriter, user);
    }
}

public static User Restore()
{
    if (!File.Exists("data.xml"))
        throw new FileNotFoundException("data.xml");

    using (var textReader = new StreamReader("data.xml"))
    {
        var xmlSerializer = new XmlSerializer(typeof(User));
        return (User)xmlSerializer.Deserialize(textReader);
    }
}

public void CreateAndSave()
{
  var character = new Character();
  character.Name = "Tranzor Z";
  character.Skills.Add(Skill.TreeClimber);

  var user = new User();
  user.Username = "Somebody";
  user.Characters.Add(character);

  Save(user);
}

public void RestoreAndPrint()
{
  var user = Restore();
  Console.WriteLine("Username: {0}", user.Username);
  Console.WriteLine("Characters: {0}", user.Characters.Count);
}

The XML generated by executing CreateAndSave() looks like so:

<User>
  <Username>Somebody</Username>
  <Characters>
    <Character>
      <Name>Tranzor Z</Name>
      <Skills>
        <Skill>TreeClimber</Skill>
      </Skills>
    </Character>
  <Characters>
</User>

Perfect! That's the way it should look. If I then execute RestoreAndPrint() I get a User object with the Username property set properly but the Characters property is an empty list:

Username: Somebody
Characters: 0

Can anybody explain to me why the Characters property is serialized properly but won't deserialize?

like image 908
Andy Avatar asked May 20 '09 21:05

Andy


3 Answers

Cannot reproduce; I get (after fixing the more immediate bugs):

Username: Somebody
Characters: 1

Changes:

  • WriteLine instead of WriteFormat (which prevented it compiling)
  • init the lists in the default constructors (which prevented CreateAndSave from working):
    • public User() { Characters = new List<Character>(); }
    • public Character() { Skills = new List<Skill>(); }
like image 161
Marc Gravell Avatar answered Sep 17 '22 18:09

Marc Gravell


In the past, when serializing lists, I've used the [XmlArray] and [XmlArrayItem] annotations. You would then put an [XmlIgnore] annotation on the Characters property. In your case, it would look something like this:

[XmlArray("Characters")]
[XmlArrayItem("Character", Type=typeof(Character))]
public Character[] _ Characters
{
    get
    {
        //Make an array of Characters to return 
        return Characters.ToArray();
    }

    set
    {
        Characters.Clear();
        for( int i = 0; i < value.Length; i++ )
            Characters.Add( value[i] );
    }
}

Hope that helps.

like image 36
Dov Avatar answered Sep 19 '22 18:09

Dov


I know this is old but i have the same problem.

What i found is that if i have an object that implements IXmlSerializable in the graph (so the main object that i want to deserialize dosen't implement the interface, but an object in it implements it), it ruins all the deserialization. Lists don't deserialize anymore, neither the object i'm talking about. Serialization works perfect.

like image 34
Alex1984 Avatar answered Sep 19 '22 18:09

Alex1984