2009-10-23 2 views
2

Я пытаюсь научить - как сериализовать/десериализовать в/из XML с помощью сериализатора на основе атрибутов. Я поставил ниже код вместе для тестирования, но кажется, что я, возможно, пропустил точку или два. Может ли кто-нибудь помочь мне и рассказать мне, что делать, чтобы все это проработало? Код ниже должен компилироваться и выполняться просто отлично, но будет генерировать исключение - что-то, вероятно, неверно с моими атрибутами.Вопрос о сериализации XML

Что я пропустил?

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

ответ

2

Попробуйте это:

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

Проблема заключается в том, что PushWorldObject принимает тип WorldObject, но вы на самом деле проходит тип MyObject. Сериализатор ничего не знает об этом типе, поэтому генерирует исключение. WorldObject используется в классе World, поэтому этот тип известен по умолчанию. Однако MyObject не используется внутри World - так что вы должны вручную объявить его как Known.

В качестве альтернативы, вы можете добавить атрибут KnownType так:

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

Я не уверен, что понимаю – Maciek

+0

Я проверяю свой код, я не уверен, что вы правильно понимаете: using (Stream s = File.Create ("output.xml")) world_serializer.WriteObject (s , мой Мир); Я передаю объект my_world в сериализатор, который имеет тип World. Я что-то упустил? – Maciek

+0

Моя ошибка, это метод PushWorldObject, который использует этот тип. –

0

Самой большой проблемой см. абстрактный объект WorldObject. Вам нужно будет использовать атрибут KnownType (или через конфигурацию или имя метода - проверьте MSDN для «Data Contract Known Types»), чтобы рассказать об известных подтипах, которые могут возникнуть (например, MyObject).

+0

уточните пожалуйста? – Maciek

+0

WorldObject на самом деле не проблема, поскольку он используется в World, так что он известен по умолчанию. –

0

Просто для уточнения: если вы сериализации XML для целей, имеющих XML в известном формате, то DataContractSerializer является плохой выбор; вам бы лучше использовать XmlSerializer, который имеет больший контроль над компоновкой xml. Вы бы использовать [XmlElement]/[XmlAttribute]/[XmlType]/[XmlRoot] атрибуты вместо [DataMember] и т.д.

Если вы просто хотите использовать XML, потому что это текст и сохраняет объект, то DataContractSerializer прекрасно; и имеет другие преимущества, например, - не требуя открытого безпараллельного конструктора (который делает XmlSerializer), и работает с частными членами (что XmlSerializer не делает).

+0

Другим ограничением XmlSerializer является то, что, если у вас есть свойства, как get, так и set accessors должны быть общедоступными. Я боролся с этим только сегодня утром! –

+0

Действительно - это особенно раздражает списки, в частности. –

Смежные вопросы