C# 序列化字典包装器时出现问题
我定义了两个类。第一个C# 序列化字典包装器时出现问题,c#,.net,xml,serialization,C#,.net,Xml,Serialization,我定义了两个类。第一个 [Serializable] public class LocalizationEntry { public LocalizationEntry() { this.CatalogName = string.Empty; this.Identifier = string.Empty; this.Translation = new Dictionary<string, string>();
[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);
}
}
}
}
最后,用于序列化的代码:
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);
}
}
问题是反序列化后反序列化dentry.TranslationsList
为空。我在本地化entry.TransalionsList的setter处设置了一个断点,它也来自反序列化程序empty。序列化的产品当然是有效的。我的代码中有什么漏洞吗
编辑:
以下是生成的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>
目录
身份证件
贾布科
苹果
阿普费尔
我猜问题与此有关:
public List<Translation> TranslationsList
公共列表翻译列表
get/set运算符仅用于获取或分配完全格式的列表。例如,如果您试图在自己的代码中使用它,每次您都会执行以下操作
TranslationsList.添加(项目)
它只是从现有的字典中创建一个新的列表,而不是实际处理您的条目。我打赌反序列化程序的工作方式大致相同:使用set
创建一次新对象,然后使用get
添加XML中的每个项。由于get
中发生的所有事情都是从字典中复制的(当您开始反序列化时,字典是空的),因此最终将一无所获
尝试将其替换为一个字段:
公共列表翻译列表代码>
然后在序列化之前显式调用代码将字典复制到此列表,并在反序列化之后将其从此列表复制到字典。假设这是可行的,您可能会找到一种更无缝的方法来实现您要做的事情。问题是Xml反序列化程序没有设置TranslationList属性。set方法将被命中,但只能通过调用this.TranslationsList=new List()来命中;在LocalisationEntry构造函数中。我还不知道为什么,但我怀疑这是因为它不知道如何将翻译对象数组转换回列表
我添加了以下代码,效果很好:
[XmlArray(ElementName = "Translations")]
public Translation[] TranslationArray
{
get
{
return TranslationsList.ToArray();
}
set
{
TranslationsList = new List<Translation>(value);
}
}
[XmlIgnore]
public List<Translation> TranslationsList
....
[XmlArray(ElementName=“Translations”)]
公共翻译
{
得到
{
返回TranslationsList.ToArray();
}
设置
{
TranslationsList=新列表(值);
}
}
[XmlIgnore]
公共列表翻译列表
....
我创建了一个示例,它允许您在使用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; }
}
类程序
{
静态void Main(字符串[]参数)
{
LocalizationEntry=新的LocalizationEntry()
{
CatalogName=“Catalog”,
Identifier=“Id”,
翻译=
{
{“PL”,“jabłko”},
{“恩”,“苹果”},
{“DE”,“apfel”}
}
};
使用(MemoryStream stream=new MemoryStream())
{
XmlSerializer serializer=新的XmlSerializer(typeof(LocalizationEntry));
serializer.Serialize(流、条目);
stream.Seek(0,SeekOrigin.Begin);
LocalizationEntry deserializedEntry=(LocalizationEntry)序列化程序。反序列化(流);
serializer.Serialize(Console.Out,反序列化dentry);
}
}
}
公共类本地化条目
{
public LocalizationEntry(){this.Translations=new TranslationCollection();}
公共字符串CatalogName{get;set;}
公共字符串标识符{get;set;}
[XmlArrayItem]
public TranslationCollection Translations{get;private set;}
}
公共类TranslationCollection
:收藏
{
公共TranslationCollection(参数Translation[]项)
{
如果(空!=项目)
{
foreach(项目中的翻译项目)
{
本条增加(项目);
}
}
}
公共void添加(字符串语言、字符串文本)
{
添加(新的翻译)
{
语言=语言,
文本=文本
});
}
}
公共课翻译
{
[XmlAttribute(AttributeName=“lang”)]
公共字符串语言{get;set;}
[XmlText]
公共字符串文本{get;set;}
}
使用XmlSerializer类本身时存在一些缺点。NET指南鼓励您不要为集合属性(如翻译列表)提供公共设置器。但是,当您查看XmlSerializer生成的代码时,您将看到它将使用Setter,而不管它是否可访问。当XmlSerializer动态加载临时类时,这会导致编译错误。避免这种情况的唯一方法是让XmlSerializer认为它实际上无法创建列表的实例,因此不会尝试为其调用set。如果XmlSerializer检测到它无法创建实例,它将抛出一个异常,而不是使用Setter,并成功编译临时类。我使用param关键字欺骗序列化程序,使其认为没有默认构造函数
此解决方案的唯一缺点是,在我的示例中,必须为属性(TranslationCollection)使用非泛型、非接口类型。d4nt的解决方案比在(反)序列化后处理更好,但感谢您的精彩解释!
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; }
}