2011-02-25 93 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)的非通用非接口类型。