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; }
}