C# 如何从不使用 XElement 的自定义 XML 序列化/反序列化为`Dictionary<int, string>`?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12554186/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 23:39:42  来源:igfitidea点击:

How to serialize/deserialize to `Dictionary<int, string>` from custom XML not using XElement?

c#.netxmlmono

提问by myWallJSON

Having empty Dictionary<int, string>how to fill it with keys and values from XML like

有空Dictionary<int, string>如何用来自 XML 的键和值填充它,例如

<items>
<item id='int_goes_here' value='string_goes_here'/>
</items>

and serialize it back into XML not using XElement?

并将其序列化回 XML 而不使用 XElement?

采纳答案by L.B

With the help of a temporary itemclass

在临时item班级的帮助下

public class item
{
    [XmlAttribute]
    public int id;
    [XmlAttribute]
    public string value;
}

Sample Dictionary:

示例词典:

Dictionary<int, string> dict = new Dictionary<int, string>()
{
    {1,"one"}, {2,"two"}
};

.

.

XmlSerializer serializer = new XmlSerializer(typeof(item[]), 
                                 new XmlRootAttribute() { ElementName = "items" });

Serialization

序列化

serializer.Serialize(stream, 
              dict.Select(kv=>new item(){id = kv.Key,value=kv.Value}).ToArray() );

Deserialization

反序列化

var orgDict = ((item[])serializer.Deserialize(stream))
               .ToDictionary(i => i.id, i => i.value);

------------------------------------------------------------------------------

-------------------------------------------------- -----------------------------

Here is how it can be done using XElement, if you change your mind.

如果您改变主意,可以使用 XElement来完成此操作

Serialization

序列化

XElement xElem = new XElement(
                    "items",
                    dict.Select(x => new XElement("item",new XAttribute("id", x.Key),new XAttribute("value", x.Value)))
                 );
var xml = xElem.ToString(); //xElem.Save(...);

Deserialization

反序列化

XElement xElem2 = XElement.Parse(xml); //XElement.Load(...)
var newDict = xElem2.Descendants("item")
                    .ToDictionary(x => (int)x.Attribute("id"), x => (string)x.Attribute("value"));

回答by Manuel Schweigert

Dictionaries are not Serializable in C# by default, I don't know why, but it seems to have been a design choice.

默认情况下,字典在 C# 中是不可序列化的,我不知道为什么,但这似乎是一种设计选择。

Right now, I'd recommend using Json.NETto convert it to JSON and from there into a dictionary (and vice versa). Unless you really need the XML, I'd recommend using JSON completely.

现在,我建议使用Json.NET将其转换为 JSON 并从那里转换为字典(反之亦然)。除非您真的需要 XML,否则我建议您完全使用 JSON。

回答by erikH

Write a class A, that contains of an array of class B. Class B should have an id property and a value property. Deserialize the xml to class A. Convert the array in A to the wanted dictionary.

编写一个类 A,其中包含一个类 B 的数组。类 B 应该有一个 id 属性和一个 value 属性。将 xml 反序列化为 A 类。将 A 中的数组转换为所需的字典。

To serialize the dictionary convert it to an instance of class A, and serialize...

要序列化字典,将其转换为类 A 的实例,然后序列化...

回答by Scott Chamberlain

Paul Welter's ASP.NET bloghas a dictionary that is serializeable. But it does not use attributes. I will explain why below the code.

Paul Welter 的ASP.NET 博客有一个可序列化的字典。但它不使用属性。我将在代码下面解释原因。

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml.Serialization;

[XmlRoot("dictionary")]
public class SerializableDictionary<TKey, TValue>
    : Dictionary<TKey, TValue>, IXmlSerializable
{
    #region IXmlSerializable Members
    public System.Xml.Schema.XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(System.Xml.XmlReader reader)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        bool wasEmpty = reader.IsEmptyElement;
        reader.Read();

        if (wasEmpty)
            return;

        while (reader.NodeType != System.Xml.XmlNodeType.EndElement)
        {
            reader.ReadStartElement("item");

            reader.ReadStartElement("key");
            TKey key = (TKey)keySerializer.Deserialize(reader);
            reader.ReadEndElement();

            reader.ReadStartElement("value");
            TValue value = (TValue)valueSerializer.Deserialize(reader);
            reader.ReadEndElement();

            this.Add(key, value);

            reader.ReadEndElement();
            reader.MoveToContent();
        }
        reader.ReadEndElement();
    }

    public void WriteXml(System.Xml.XmlWriter writer)
    {
        XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
        XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));

        foreach (TKey key in this.Keys)
        {
            writer.WriteStartElement("item");

            writer.WriteStartElement("key");
            keySerializer.Serialize(writer, key);
            writer.WriteEndElement();

            writer.WriteStartElement("value");
            TValue value = this[key];
            valueSerializer.Serialize(writer, value);
            writer.WriteEndElement();

            writer.WriteEndElement();
        }
    }
    #endregion
}

First, there is one gotcha with this code. Say you read a dictionary from another source that has this:

首先,这段代码有一个问题。假设您从另一个来源阅读了一本字典,其中包含以下内容:

<dictionary>
  <item>
    <key>
      <string>key1</string>
    </key>
    <value>
      <string>value1</string>
    </value>
  </item>
  <item>
    <key>
      <string>key1</string>
    </key>
    <value>
      <string>value2</string>
    </value>
  </item>
</dictionary>

This will throw a exception on de-seariazation because you can only have one key for a dictionary.

这将在 de-seariazation 时引发异常,因为您只能有一个字典的键。



The reason you MUST use a XElement in a seriazed dictionary is dictionary is not defined as Dictionary<String,String>, a dictionary is Dictionary<TKey,TValue>.

您必须在序列化字典中使用 XElement 的原因是字典没有定义为Dictionary<String,String>,字典是Dictionary<TKey,TValue>

To see the problem, ask your self: Lets say we have a TValuethat serializes in to something that uses Elements it describes itself as XML (lets say a dictionary of dictionaries Dictionary<int,Dictionary<int,string>>(not that uncommon of a pattern, it's a lookup table)), how would your Attribute only version represent a dictionary entirely inside a attribute?

要看到这个问题,问问你自己:假设我们有一个TValue序列化到使用元素的东西,它把自己描述为 XML(假设一个字典字典Dictionary<int,Dictionary<int,string>>(不是那么不常见的模式,它是一个查找表)),如何你的 Attribute only 版本会代表一个完全在属性内的字典吗?

回答by Michael G

I use serializable classes for the WCF communication between different modules. Below is an example of serializable class which serves as DataContract as well. My approach is to use the power of LINQ to convert the Dictionary into out-of-the-box serializable List<> of KeyValuePair<>:

我使用可序列化的类进行不同模块之间的 WCF 通信。下面是一个可序列化类的示例,它也用作 DataContract。我的方法是利用 LINQ 的强大功能将 Dictionary 转换为开箱即用的 KeyValuePair<> 的可序列化 List<>:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Runtime.Serialization;
    using System.Xml.Serialization;

    namespace MyFirm.Common.Data
    {
        [DataContract]
        [Serializable]
        public class SerializableClassX
        {
            // since the Dictionary<> class is not serializable,
            // we convert it to the List<KeyValuePair<>>
            [XmlIgnore]
            public Dictionary<string, int> DictionaryX 
            {
                get
                {
                    return  SerializableList == null ? 
                            null :
                            SerializableList.ToDictionary(item => item.Key, item => item.Value);
                }

                set
                {
                    SerializableList =  value == null ?
                                        null :
                                        value.ToList();
                }
            }

            [DataMember]
            [XmlArray("SerializableList")]
            [XmlArrayItem("Pair")]
            public List<KeyValuePair<string, int>> SerializableList { get; set; }
        }
    }

The usage is straightforward - I assign a dictionary to my data object's dictionary field - DictionaryX. The serialization is supported inside the SerializableClassX by conversion of the assigned dictionary into the serializable List<> of KeyValuePair<>:

用法很简单——我为我的数据对象的字典字段分配了一个字典——DictionaryX。通过将分配的字典转换为 KeyValuePair<> 的可序列化 List<>,SerializableClassX 内部支持序列化:

    // create my data object
    SerializableClassX SerializableObj = new SerializableClassX(param);

    // this will call the DictionaryX.set and convert the '
    // new Dictionary into SerializableList
    SerializableObj.DictionaryX = new Dictionary<string, int>
    {
        {"Key1", 1},
        {"Key2", 2},
    };

回答by Jbjstam

Based on L.B.'s answer.

基于LB的回答。

Usage:

用法:

var serializer = new DictionarySerializer<string, string>();
serializer.Serialize("dictionary.xml", _dictionary);
_dictionary = _titleDictSerializer.Deserialize("dictionary.xml");

Generic class:

通用类:

public class DictionarySerializer<TKey, TValue>
{
    [XmlType(TypeName = "Item")]
    public class Item
    {
        [XmlAttribute("key")]
        public TKey Key;
        [XmlAttribute("value")]
        public TValue Value;
    }

    private XmlSerializer _serializer = new XmlSerializer(typeof(Item[]), new XmlRootAttribute("Dictionary"));

    public Dictionary<TKey, TValue> Deserialize(string filename)
    {
        using (FileStream stream = new FileStream(filename, FileMode.Open))
        using (XmlReader reader = XmlReader.Create(stream))
        {
            return ((Item[])_serializer.Deserialize(reader)).ToDictionary(p => p.Key, p => p.Value);
        }
    }

    public void Serialize(string filename, Dictionary<TKey, TValue> dictionary)
    {
        using (var writer = new StreamWriter(filename))
        {
            _serializer.Serialize(writer, dictionary.Select(p => new Item() { Key = p.Key, Value = p.Value }).ToArray());
        }
    }
}

回答by Wojtpl2

You can use ExtendedXmlSerializer. If you have a class:

您可以使用ExtendedXmlSerializer。如果你有一个类:

public class TestClass
{
    public Dictionary<int, string> Dictionary { get; set; }
}

and create instance of this class:

并创建此类的实例:

var obj = new TestClass
{
    Dictionary = new Dictionary<int, string>
    {
        {1, "First"},
        {2, "Second"},
        {3, "Other"},
    }
};

You can serialize this object using ExtendedXmlSerializer:

您可以使用 ExtendedXmlSerializer 序列化此对象:

var serializer = new ConfigurationContainer()
    .UseOptimizedNamespaces() //If you want to have all namespaces in root element
    .Create();

var xml = serializer.Serialize(
    new XmlWriterSettings { Indent = true }, //If you want to formated xml
    obj);

Output xml will look like:

输出 xml 将如下所示:

<?xml version="1.0" encoding="utf-8"?>
<TestClass xmlns:sys="https://extendedxmlserializer.github.io/system" xmlns:exs="https://extendedxmlserializer.github.io/v2" xmlns="clr-namespace:ExtendedXmlSerializer.Samples;assembly=ExtendedXmlSerializer.Samples">
  <Dictionary>
    <sys:Item>
      <Key>1</Key>
      <Value>First</Value>
    </sys:Item>
    <sys:Item>
      <Key>2</Key>
      <Value>Second</Value>
    </sys:Item>
    <sys:Item>
      <Key>3</Key>
      <Value>Other</Value>
    </sys:Item>
  </Dictionary>
</TestClass>

You can install ExtendedXmlSerializer from nugetor run the following command:

您可以从nuget安装 ExtendedXmlSerializer或运行以下命令:

Install-Package ExtendedXmlSerializer

回答by xmedeko

I have a struct KeyValuePairSerializable:

我有一个结构KeyValuePairSerializable

[Serializable]
public struct KeyValuePairSerializable<K, V>
{
    public KeyValuePairSerializable(KeyValuePair<K, V> pair)
    {
        Key = pair.Key;
        Value = pair.Value;
    }

    [XmlAttribute]
    public K Key { get; set; }

    [XmlText]
    public V Value { get; set; }

    public override string ToString()
    {
        return "[" + StringHelper.ToString(Key, "") + ", " + StringHelper.ToString(Value, "") + "]";
    }
}

Then, the XML serialization of a Dictionaryproperty is by:

然后,Dictionary属性的 XML 序列化是通过:

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

[XmlArray("Parameters")]
[XmlArrayItem("Pair")]
[DebuggerBrowsable(DebuggerBrowsableState.Never)] // not necessary
public KeyValuePairSerializable<string, string>[] ParametersXml
{
    get 
    { 
        return Parameters?.Select(p => new KeyValuePairSerializable<string, string>(p)).ToArray(); 
    }
    set
    {
        Parameters = value?.ToDictionary(i => i.Key, i => i.Value);
    }
}

Just the property must be the array, not the List.

只是属性必须是数组,而不是列表。

回答by Malick

There is an easy way with Sharpeserializer (open source) :

Sharpeserializer(开源)有一个简单的方法:

http://www.sharpserializer.com/

http://www.sharpserializer.com/

It can directly serialize/de-serialize dictionary.

它可以直接序列化/反序列化字典。

There is no need to mark your object with any attribute, nor do you have to give the object type in the Serialize method (See here).

无需使用任何属性标记您的对象,也无需在 Serialize 方法中指定对象类型(请参阅此处)。

To install via nuget : Install-package sharpserializer

通过 nuget 安装: Install-package sharpserializer

Then it is very simple :

然后很简单:

Hello World(from the official website):

Hello World(来自官网):

// create fake obj
var obj = createFakeObject();

// create instance of sharpSerializer
// with standard constructor it serializes to xml
var serializer = new SharpSerializer();

// serialize
serializer.Serialize(obj, "test.xml");

// deserialize
var obj2 = serializer.Deserialize("test.xml");

回答by Smartoweb

KeyedCollection works like dictionary and is serializable.

KeyedCollection 像字典一样工作,并且是可序列化的。

First create a class containing key and value:

首先创建一个包含键和值的类:

/// <summary>
/// simple class
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCulture
{
    /// <summary>
    /// culture
    /// </summary>
    public string culture;

    /// <summary>
    /// word list
    /// </summary>
    public List<string> list;

    /// <summary>
    /// status
    /// </summary>
    public string status;
}

then create a class of type KeyedCollection, and define a property of your class as key.

然后创建一个 KeyedCollection 类型的类,并将类的一个属性定义为键。

/// <summary>
/// keyed collection.
/// </summary>
/// <remarks></remarks>
[Serializable()]
public class cCultures : System.Collections.ObjectModel.KeyedCollection<string, cCulture>
{
    protected override string GetKeyForItem(cCulture item)
    {
        return item.culture;
    }
}

Usefull to serialize such type of datas.

用于序列化此类数据。