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

白昼怎懂夜的黑 提交于 2020-01-03 12:34:36

问题


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?


回答1:


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>(); }



回答2:


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.




回答3:


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.




回答4:


Perhaps, instead of a StreamReader, create an XmlReader, instead. Also, as a troubleshooting step, you might try your existing code with explicit types (as below) instead of "var" declarations.

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

  XmlReader xr = XmlReader.Create("data.xml");
  XmlSerializer serializer = new XmlSerializer(typeof(User));
  var user = (User)serializer.Deserialize(xr);
  xr.Close();
  return user;
}

EDIT: Also, try the XmlInclude annotation on your User class.

[XmlInclude( typeof( Character ) )]



回答5:


After futzing around with the code for a long time I finally gave up on the idea of using the default behaviour of the XmlSerializer.

All of the relevant classes now inherit IXmlSerializer and implement the ReadXml() and WriteXml() functions. I was really hoping to take the lazy route but now that I've played with IXmlSerializer I realize that it really isn't difficult at all and the added flexibility is really nice.




回答6:


Stream stream = File.Open(filename + ".xml", FileMode.Open);
User user = null;
using (XmlReader reader = XmlReader.Create(stream))
{
    user = IntermediateSerializer.Deserialize<User>(reader, null);
}
stream.Close();

return user;

I would try using something like this.




回答7:


I added this attribute to my List types and it worked then (had your same problem):

[System.Xml.Serialization.XmlElementAttribute("NameOfMyField")]
public List<SomeType> NameOfMyField{get;set;}

I think this is your solution.




回答8:


It might have to do with the deserializing of the enum....since it serialized it as the string value...might have trouble creating the correct enum value for the character. Have not tested this hypothesis...



来源:https://stackoverflow.com/questions/890316/xmlserializer-deserialize-isnt-deserializing-a-listt-correctly

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!