2009-05-20 11 views
5

Soy nuevo en el C# XmlSerializer, así que podría estar perdiendo algo básico aquí.XmlSerializer.Deserialize() no está deserializando una Lista <T> correctamente

El problema que me estoy encontrando es que tengo una clase que tiene un List<T> de otra clase. Cuando serializo la clase principal, el XML se ve hermoso y todos los datos están intactos. Cuando deserializo el XML, los datos en el List<T> desaparecen y me queda un List<T> vacío. No recibo ningún error y la parte de serialización funciona como encanto.

¿Qué me está perdiendo con el proceso de deserialización?

EDITAR: Tenga en cuenta que el código que se muestra a continuación no reproduce el problema, funciona. Esta fue una versión simplificada del código real, que hizo no trabajo. Lamentablemente, el código siguiente se simplificó lo suficiente como para no reproducir el problema.

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

El XML generado mediante la ejecución de CreateAndSave() se ve así:

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

perfecto! Esa es la forma en que debería verse. Pues si yo ejecuto RestoreAndPrint() consigo un objeto de usuario con la propiedad nombre de usuario configurado correctamente pero la propiedad personajes es una lista vacía:

Username: Somebody 
Characters: 0 

Puede alguien explicarme por qué la propiedad Characters se serializa correctamente, pero no va a deserializar?

+3

no necesita [Serializable] en los tipos. El comportamiento del Xml Serializer es completamente independiente de ese atributo. – Cheeso

+0

OK; así que nosotros (o yo, al menos) sospechamos que el problema está en el código "real" (no se muestra); ¿Hace algo interesante en el acceso "establecido" de las listas? Ese sería el primer lugar donde miraría. –

+0

No, todas son propiedades básicas sin nada de lujos en los accesadores get o set. Todos se ven así: public T PropertyName {get; conjunto; } – Andy

Respuesta

1

Después de pelearse con el código por un largo tiempo, finalmente me di por vencido en la idea de utilizar el comportamiento predeterminado de la XmlSerializer.

Todas las clases relevantes ahora heredan IXmlSerializer e implementan las funciones ReadXml() y WriteXml(). Realmente estaba esperando tomar la ruta floja pero ahora que he jugado con IXmlSerializer me doy cuenta de que realmente no es nada difícil y la flexibilidad añadida es realmente agradable.

-2

Podría tener que ver con la deserialización de la enumeración ... ya que la serializó como el valor de la cadena ... podría tener problemas para crear el valor de enum correcto para el carácter. No he probado esta hipótesis ...

+0

En mi proyecto de código real, la clase raíz tiene otras enumeraciones que se serializan y deserializan correctamente. También puedo serializar y deserializar los objetos individuales dentro de la lista de Caracteres. Solo cuando deserializa un objeto Usuario, el proceso se rompe y la lista de Caracteres está vacía y no se lanzan excepciones. – Andy

+0

-1: no hay ningún problema con la enumeración. Recomendaría en el futuro cualquier prueba, o no decir nada. –

0
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; 

Intentaré utilizar algo como esto.

+0

El código con el que estoy trabajando * está * relacionado con el juego, pero no estoy haciendo referencia al XNA Framework en este proyecto, así que no tengo acceso al IntermediateSerializer. – Andy

+0

Lo siento, hice la suposición. –

5

No se puede reproducir; Recibo (después de corregir los fallos más inmediatos):

Username: Somebody 
Characters: 1 

Cambios:

  • WriteLine en lugar de WriteFormat (lo que impidió que la compilación)
  • init las listas de los constructores por defecto (que impedían CreateAndSave de trabajo):
    • public User() { Characters = new List<Character>(); }
    • public Character() { Skills = new List<Skill>(); }
+1

Hrm ... El código que publiqué anteriormente era un código de muestra que imitaba mi código real, de ahí mis errores, perdón por eso. Mi proyecto real es significativamente más grande, pero el código publicado es una aproximación muy cercana a lo que estoy tratando de hacer. Mi intención era proporcionar un ejemplo que represente mi situación sin proporcionar todos los detalles sangrientos. :/ – Andy

+0

Lamentablemente, creo que el problema está en el código omitido. –

+0

Heh.Echaré un vistazo a una red de protobuf, pero mi objetivo para este proyecto era un formato legible/editable por humanos que pudiera manipularse con cualquier editor de texto básico. – Andy

2

En el pasado, cuando la serialización de las listas, he utilizado los [XmlArray] anotaciones y [XmlArrayItem]. Luego colocaría una anotación [XmlIgnore] en la propiedad Characters. En su caso, se vería algo como esto:

[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]); 
    } 
} 

Espero que ayude.

+0

Sin suerte: obtengo el mismo resultado, es decir, una lista vacía. Cuando me deserializo, no se toca ninguno de los códigos relacionados con el Personaje. Es como si todo el nodo Caracteres en el XML estuviera siendo descartado sin ser procesado. – Andy

1

Quizás, en lugar de un StreamReader, cree un XmlReader, en su lugar. Además, como un paso de solución de problemas, puede probar su código existente con tipos explícitos (como a continuación) en lugar de declaraciones "var".

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: Además, trate la anotación XmlInclude en su clase de usuario.

[XmlInclude(typeof(Character))] 
+0

Agregué XmlIncludeAttributes a todas las clases relevantes y la deserialización aún falló. – Andy

0

que añade este atributo a mis tipos de lista y funcionó entonces (tenía el mismo problema):

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

Creo que esta es la solución.

1

Sé que esto es viejo, pero tengo el mismo problema.

Lo que encontré es que si tengo un objeto que implementa IXmlSerializable en el gráfico (por lo que el objeto principal que deseo deserializar no implementa la interfaz, pero un objeto en ella lo implementa), arruina todo el deserialización. Las listas ya no se deserializan, ni el objeto del que estoy hablando. La serialización funciona perfecto.

+3

descubrió por qué: en ReadXml, tenga cuidado de llevar al lector a la etiqueta de cierre. –

+1

hace una muy buena observación que es crítica si has implementado IXmlSerializable - asegúrate de terminar con reader.ReadEndElement(); al final de su lógica personalizada de lector xml –

+0

Desearía poder halagarles a los dos diez veces ... ¿Por qué en la Tierra el artículo de MSDN que copié el código de forma literal tiene este error? – vines

Cuestiones relacionadas