我定义了两个类。第一个......
[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 :(得分:2)
问题是您的TranslationList属性未由Xml反序列化程序设置。 set方法将被命中,但只能通过调用this.TranslationsList = new List();在LocalisationEntry构造函数中。我还不确定为什么,但我怀疑它是因为它不知道如何将一组Translation对象转换回List。
我添加了以下代码,它运行良好:
[XmlArray(ElementName = "Translations")]
public Translation[] TranslationArray
{
get
{
return TranslationsList.ToArray();
}
set
{
TranslationsList = new List<Translation>(value);
}
}
[XmlIgnore]
public List<Translation> TranslationsList
....
答案 1 :(得分:1)
我猜这个问题与此有关:
public List<Translation> TranslationsList
get / set运算符仅用于获取或分配完整形式的列表。例如,如果您尝试在自己的代码中使用它,那么每次执行类似
的操作时 TranslationsList.Add(item)
它只会从现有字典中创建一个新列表,而不会实际处理您的项目。我打赌反序列化器的工作原理大致相同:使用set
创建一次新对象,然后使用get
,因为它添加了XML中的每个项目。因为get
中发生的所有事情都是字典中的副本(当你开始反序列化时它是空的)你最终什么都没有。
尝试用一个字段替换它:
public List<Translation> TranslationsList;
然后在序列化之前显式调用代码将字典复制到此列表,并在反序列化后将其从此列表复制到字典中。假设有效,你可以找到一种更无缝的方式来实现你想要做的事情。
答案 2 :(得分: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-keyword来欺骗序列化程序,认为没有默认构造函数。
此解决方案的唯一缺点是您必须在我的示例中为属性(TranslationCollection)使用非泛型的非接口类型。