2009-10-23 2 views
2

J'essaie de m'apprendre à sérialiser/désérialiser en/à partir de XML en utilisant le sérialiseur par attributs. J'ai mis le code ci-dessous ensemble à des fins de test, mais il semble que j'ai peut-être manqué un point ou deux. Quelqu'un peut-il m'aider et me dire quoi faire pour que tout fonctionne correctement? Le code ci-dessous doit compiler et s'exécuter correctement, mais il y aura une exception - quelque chose ne va probablement pas avec mes attributs.Question sur la sérialisation XML

Qu'est-ce que j'ai manqué?

Program.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.Serialization; 
using System.IO; 

namespace XMLSerialisation_test 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      World my_world = new World(new Point(20, 30)); 

      for (int i = 0; i < 10; i++) 
      { 
       string property = String.Format("Property no.{0}", i); 
       my_world.PushWorldObject(new MyObject(new Point(i, i), property)); 
      } 

      DataContractSerializer world_serializer = new DataContractSerializer(typeof(World)); 

      try 
      { 
       using (Stream s = File.Create("output.xml")) 
        world_serializer.WriteObject(s, my_world); 
      } 
      catch (Exception e) 
      { 
       Console.WriteLine("Exception occured : {0}", e.Message); 
      } 

     } 
    } 
} 

WorldObject.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.Serialization; 

namespace XMLSerialisation_test 
{ 
    [DataContract] 
    public struct Point // couldn't find the pre-defined Point for some reason 
    { 
     public Point(double x, double y) 
     { X = x; Y = y; } 
     [DataMember] 
     public double X; 
     [DataMember] 
     public double Y; 
    } 

    [DataContract] 
    public abstract class WorldObject 
    { 
     public WorldObject() : this(0.0, 0.0) 
     {} 

     public WorldObject(Point loc) 
     { m_location = loc; } 

     public WorldObject(double x, double y) 
     { 
      m_location.X = x; 
      m_location.Y = y; 
     } 

     [DataMember] 
     public Point Location 
     { 
      get { return m_location; } 
      set { m_location = value; } 
     } 

     protected Point m_location; 
    } 

    [DataContract] 
    public class MyObject : WorldObject 
    { 
     public MyObject(string prop) 
      : base(0.0, 0.0) 
     { m_property = prop; } 

     public MyObject(Point p, string prop) 
      : base(p) 
     { m_property = prop; } 

     [DataMember] 
     public string Property 
     { 
      get{ return m_property; } 
      set{ m_property = value; } 
     } 

     private string m_property; 
    } 
} 

World.cs

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Runtime.Serialization; 
using System.Xml.Serialization; 

namespace XMLSerialisation_test 
{ 
    [DataContract] 
    class World 
    { 
     public World() : this(new Point(10, 10)) 
     { } 

     public World(Point size) 
     { m_world_size = size; } 

     public bool PushWorldObject(WorldObject o) 
     { 
      try 
      { 
       WorldObjects.Add(o); 
       return true; 
      } 
      catch(Exception e) 
      { 
       Console.WriteLine("Exception occured : {0}", e.Message); 
       return false; } 
     } 


     #region Accessors 
     [DataMember] 
     public List<WorldObject> WorldObjects 
     { 
      get { return m_world_objects; } 
      set { m_world_objects = value; } 
     } 
     [DataMember] 
     public Point WorldSize 
     { 
      get { return m_world_size; } 
      private set { m_world_size = value; } 
     } 
     #endregion 

     #region Fields 
     private List<WorldObject> m_world_objects = new List<WorldObject>(); 
     private Point m_world_size; 
     #endregion 
    } 
} 

Répondre

2

Essayez ceci:

DataContractSerializer world_serializer = new DataContractSerializer(typeof(World), new List<Type> { typeof(MyObject) }); 

Le problème est que PushWorldObject prend le type WorldObject, mais vous êtes en fait le type MyObject passait. Le sérialiseur ne sait rien de ce type, donc lance une exception. WorldObject est utilisé dans la classe World, donc ce type est connu par défaut. Cependant, MyObject n'est pas utilisé dans World - vous devez donc le déclarer manuellement comme étant connu.

Comme alternative, vous pouvez ajouter l'attribut KnownType comme ceci:

[DataContract, KnownType(typeof(MyObject))] 
class World 
{ 
+0

Je ne suis pas sûr que je comprends – Maciek

+0

Je suis examinig mon code, je ne suis pas sûr de vous comprendre à droite: en utilisant (Stream s = File.Create ("output.xml")) world_serializer.WriteObject (s , mon monde); Je passe l'objet my_world dans le sérialiseur, qui est de type World. Est-ce que j'ai râté quelque chose ? – Maciek

+0

Mon erreur, c'est la méthode PushWorldObject qui utilise le type. –

0

Le plus gros problème que je voir est le WorldObject abstrait. Vous devrez utiliser un attribut KnownType ici (ou via le nom de configuration ou de configuration - vérifier MSDN pour "Data Contract Known Types") pour lui indiquer les sous-types connus qu'il peut rencontrer (par exemple, MyObject).

+0

élaborer s'il vous plaît? – Maciek

+0

WorldObject n'est pas réellement un problème car il est utilisé dans World, il est donc connu par défaut. –

0

Juste pour clarifier: si vous sérialisation XML aux fins d'avoir xml dans un format connu, puis DataContractSerializer est un mauvais choix; vous feriez mieux d'utiliser XmlSerializer, qui a plus de contrôle sur la mise en page xml. Vous utiliserez le [XmlElement]/[XmlAttribute]/[XmlType]/[XmlRoot] attributs au lieu de [DataMember] etc.

Si vous voulez juste utiliser xml, car il est un texte et stocke l'objet, puis DataContractSerializer est très bien; et a d'autres avantages, par exemple - ne pas exiger un constructeur public sans paramètre (ce qui fait XmlSerializer) et travailler avec des membres privés (ce qui n'est pas le cas pour XmlSerializer).

+0

Une autre limitation de XmlSerializer est que si vous avez des propriétés, les accesseurs get et set doivent être publics. Je me battais juste ce matin! –

+0

En effet - cela est extrêmement ennuyeux pour les listes, en particulier. –