2011-02-25 52 views
2

我定義了兩個類。第一個...序列化字典封裝的問題

[Serializable] 
public class LocalizationEntry 
{ 
    public LocalizationEntry() 
    { 
     this.CatalogName = string.Empty; 
     this.Identifier = string.Empty; 
     this.Translation = new Dictionary<string, string>(); 
     this.TranslationsList = new List<Translation>(); 
    } 

    public string CatalogName 
    { 
     get; 
     set; 
    } 

    public string Identifier 
    { 
     get; 
     set; 
    } 

    [XmlIgnore] 
    public Dictionary<string, string> Translation 
    { 
     get; 
     set; 
    } 

    [XmlArray(ElementName = "Translations")] 
    public List<Translation> TranslationsList 
    { 
     get 
     { 
      var list = new List<Translation>(); 

      foreach (var item in this.Translation) 
      { 
       list.Add(new Translation(item.Key, item.Value)); 
      } 

      return list; 
     } 
     set 
     { 
      foreach (var item in value) 
      { 
       this.Translation.Add(item.Language, item.Text); 
      } 
     } 
    } 
} 

...其中public List<Translation> TranslationsList是不可序列public Dictionary<string, string> Translation的包裝。的鍵和值

對被定義如下:

[Serializable] 
public class Translation 
{ 
    [XmlAttribute(AttributeName = "lang")] 
    public string Language 
    { 
     get; 
     set; 
    } 

    [XmlText] 
    public string Text 
    { 
     get; 
     set; 
    } 

    public Translation() 
    { 

    } 

    public Translation(string language, string translation) 
    { 
     this.Language = language; 
     this.Text = translation; 
    } 
} 

在用來序列最後一個代碼:

static void Main(string[] args) 
{ 
    LocalizationEntry entry = new LocalizationEntry() 
    { 
     CatalogName = "Catalog", 
     Identifier = "Id", 
    }; 

    entry.Translation.Add("PL", "jabłko"); 
    entry.Translation.Add("EN", "apple"); 
    entry.Translation.Add("DE", "apfel"); 

    using (FileStream stream = File.Open(@"C:\entry.xml", FileMode.Create)) 
    { 
     XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry)); 
     serializer.Serialize(stream, entry); 
    } 

    LocalizationEntry deserializedEntry; 
    using (FileStream stream = File.Open(@"C:\entry.xml", FileMode.Open)) 
    { 
     XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry)); 
     deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream); 
    } 
} 

的問題是,反序列化後deserializedEntry.TranslationsList是空的。我在LocalizationEntry.TransalionsList的setter中設置了一個斷點,它也來自解串器空。序列化產品當然是有效的。我的代碼有沒有差距?

編輯:

下面是生成的XML:

<?xml version="1.0"?> 
<LocalizationEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> 
    <CatalogName>Catalog</CatalogName> 
    <Identifier>Id</Identifier> 
    <Translations> 
    <Translation lang="PL">jabłko</Translation> 
    <Translation lang="EN">apple</Translation> 
    <Translation lang="DE">apfel</Translation> 
    </Translations> 
</LocalizationEntry> 
+0

XML的外觀如何? – Nick 2011-02-25 12:48:52

+0

剛剛在 – rotman 2011-02-25 12:51:52

回答

2

問題是您的TranslationList屬性沒有被Xml Deserializer設置。 set方法將被打中,但只能通過調用this.TranslationsList = new List();在LocalisationEntry構造函數中。我還不確定爲什麼,但我懷疑這是因爲它不知道如何將一個Translate對象數組轉換回List。

添加以下代碼,它工作得很好:

[XmlArray(ElementName = "Translations")] 
public Translation[] TranslationArray 
{ 
    get 
    { 
     return TranslationsList.ToArray(); 
    } 

    set 
    { 
     TranslationsList = new List<Translation>(value); 
    } 
} 

[XmlIgnore] 
public List<Translation> TranslationsList 
.... 
1

我猜這個問題必須做這個:

public List<Translation> TranslationsList 

了get/set運營商只設計的東西獲取或分配完整列表。如果您嘗試在自己的代碼中使用這一點,例如,每一次你會做這樣的事情

TranslationsList.Add(item)

這純粹創建現有的字典一個新的列表,並沒有真正解決您的項目。我敢打賭,解串器的工作方式大致相同:使用set創建一個新對象,然後使用get,因爲它添加了XML中的每個項目。由於get發生的所有事情都是從字典中複製的(當你開始反序列化時它是空的),你最終什麼都沒有。

嘗試只是一個現場更換此:

public List<Translation> TranslationsList;

,然後顯式調用代碼字典複製到這個列表中,您序列化之前,從這個列表複製到詞典中,你反序列化後。假設有效,你可以想出一個更加無縫的方式來實現你想要做的事情。

+0

以上添加d4nt的解決方案比(de)序列化後的應對更好,但感謝您的好解釋! – rotman 2011-02-25 13:24:14

0

我創建了一個樣本,使用XmlSerializer時,這將讓你避免不必要的隱藏屬性:

class Program 
{ 
    static void Main(string[] args) 
    { 
     LocalizationEntry entry = new LocalizationEntry() 
     { 
      CatalogName = "Catalog", 
      Identifier = "Id", 
      Translations = 
      { 
       { "PL", "jabłko" }, 
       { "EN", "apple" }, 
       { "DE", "apfel" } 
      } 
     }; 

     using (MemoryStream stream = new MemoryStream()) 
     { 
      XmlSerializer serializer = new XmlSerializer(typeof(LocalizationEntry)); 
      serializer.Serialize(stream, entry); 

      stream.Seek(0, SeekOrigin.Begin); 
      LocalizationEntry deserializedEntry = (LocalizationEntry)serializer.Deserialize(stream); 
      serializer.Serialize(Console.Out, deserializedEntry); 
     } 
    } 
} 

public class LocalizationEntry 
{ 
    public LocalizationEntry() { this.Translations = new TranslationCollection(); } 
    public string CatalogName { get; set; } 
    public string Identifier { get; set; } 

    [XmlArrayItem] 
    public TranslationCollection Translations { get; private set; } 
} 

public class TranslationCollection 
    : Collection<Translation> 
{ 
    public TranslationCollection(params Translation[] items) 
    { 
     if (null != items) 
     { 
      foreach (Translation item in items) 
      { 
       this.Add(item); 
      } 
     } 
    } 

    public void Add(string language, string text) 
    { 
     this.Add(new Translation 
     { 
      Language = language, 
      Text = text 
     }); 
    } 
} 

public class Translation 
{ 
    [XmlAttribute(AttributeName = "lang")] 
    public string Language { get; set; } 

    [XmlText] 
    public string Text { get; set; } 
} 

使用XmlSerializer類本身時有一些缺點。這個。NET準則鼓勵你不要爲收藏屬性提供公共設置者(比如你的翻譯列表)。但是當您查看由XmlSerializer生成的代碼時,您會發現它將使用Setter,而不管它是否可訪問。當臨時類由XmlSerializer動態加載時,會導致編譯錯誤。避免這種情況的唯一方法是讓XmlSerializer認爲它不能真正創建列表的一個實例,因此不會嘗試爲它調用set。如果XmlSerializer檢測到它不能創建實例,它將拋出一個異常而不是使用Setter,並且臨時類被編譯成功。我用param關鍵字欺騙序列化程序,認爲沒有默認構造函數。

這個解決方案的唯一缺點是您必須在我的示例中使用屬性(TranslationCollection)的非通用非接口類型。