2009-10-23 151 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不在世界範圍內使用,因此您必須手動將其聲明爲已知。

作爲替代方案,可以添加KnownType屬性像這樣:

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

我不知道我明白 – Maciek 2009-10-23 20:07:24

+0

我正在檢查我的代碼,我不確定我是否理解你的權利: using(Stream s = File.Create(「output.xml」)) world_serializer.WriteObject(s , 我的世界); 我將my_world對象傳遞給序列化程序,它是World類型的。我在這裏錯過了什麼嗎? – Maciek 2009-10-23 20:17:25

+0

我的錯誤是使用該類型的方法PushWorldObject。 – 2009-10-23 20:20:51

0

最大的問題我請參閱抽象的WorldObject。您需要在那裏使用KnownType屬性(或者通過配置或方法名稱 - 檢查MSDN中的「Data Contract Known Types」),以告知它可能遇到的已知子類型(例如,MyObject)。

+0

請詳述一下嗎? – Maciek 2009-10-23 20:06:47

+0

WorldObject實際上並不是一個問題,因爲它在World中使用,所以默認情況下已知。 – 2009-10-23 20:12:42

0

只是爲了澄清:如果序列化到XML 用於以已知的格式具有XML的目的,然後DataContractSerializer是不好的選擇;您最好使用XmlSerializer,它可以更好地控制xml佈局。你會使用[XmlElement]/[XmlAttribute]/[XmlType]/[XmlRoot]屬性,而不是[DataMember]

如果你只是想使用XML,因爲它是文本和存儲對象,然後DataContractSerializer是好的;並且具有其他優點,例如 - 不要求公共無參數構造函數(其中XmlSerializer確實),並且與私有成員一起工作(其中XmlSerializer沒有)。

+0

XmlSerializer的另一個限制是,如果您有屬性,則get和set訪問器必須是公共的。我今天早上正在與此搏鬥! – 2009-10-23 20:16:59

+0

的確如此 - 特別是這對於列表來說非常煩人。 – 2009-10-23 20:20:10