【问题标题】:How to serialize/deserialize to `Dictionary<int, string>` from custom XML not using XElement?如何从不使用 XElement 的自定义 XML 序列化/反序列化为 `Dictionary<int, string>`?
【发布时间】:2012-09-15 06:32:36
【问题描述】:

有空的Dictionary&lt;int, string&gt; 如何用来自 XML 的键和值来填充它,比如

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

并在不使用 XElement 的情况下将其序列化回 XML?

【问题讨论】:

  • 解释为什么您不想要特定解决方案会很有帮助(即使 XElement 可能不是正确的方法)。
  • 为什么不想使用 XElement?序列化/反序列化可以用 linq 在一行中完成。

标签: c# .net xml mono


【解决方案1】:

借助一个临时的item 类

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

示例字典:

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

.

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

序列化

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

反序列化

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

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

如果您改变主意,使用 XElement 可以做到这一点。

序列化

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(...);

反序列化

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

【讨论】:

  • 我在循环中使用你的代码。而且我有内存泄漏。我添加了 XmlSerializer.dispose()。但是没有变化……正常吗?
  • XmlSerializer 的一些构造函数泄漏内存(临时生成的程序集) - 你应该缓存它们参见support.microsoft.com/en-us/help/886385/…
  • @L.B 是否可以扩展它以序列化 Dictionary> ?我做不到,谢谢。
【解决方案2】:

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

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
}

首先,此代码有一个问题。假设您从另一个来源阅读了一本字典:

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

这将在反序列化时引发异常,因为字典只能有一个键。


您必须在序列化字典中使用 XElement 的原因是字典未定义为 Dictionary&lt;String,String&gt;,字典为 Dictionary&lt;TKey,TValue&gt;。

要看到问题,问问你自己:假设我们有一个TValue,它序列化为使用元素的东西,它把自己描述为 XML(假设一个字典字典 Dictionary&lt;int,Dictionary&lt;int,string&gt;&gt;(这种模式并不罕见) ,它是一个查找表)),您的 Attribute only 版本将如何完全表示一个属性内的字典?

【讨论】:

  • 您通过调用“ReadXml”方法进行反序列化。
  • 您想要WriteStartElement("item") 和ReadStartElement("item") 等等在 foreach 循环中,对吧?
  • 能否请您举例说明如何使用这个类?
【解决方案3】:

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

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

【讨论】:

  • 据我了解,他想将 XML 反序列化为字典。如果 Dictionary 是可序列化的,您可以使用 XmlSerializer 等内置类来完成任务,但正如我所说,它不是。
【解决方案4】:

基于 L.B. 的回答。

用法:

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

通用类:

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

【讨论】:

    【解决方案5】:

    我有一个结构 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, "") + "]";
        }
    }
    

    那么,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);
        }
    }
    

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

    【讨论】:

      【解决方案6】:

      您可以使用ExtendedXmlSerializer。 如果你有课:

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

      并创建此类的实例:

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

      您可以使用 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);
      

      输出 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>
      

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

      Install-Package ExtendedXmlSerializer
      

      【讨论】:

        【解决方案7】:

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

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

        【讨论】:

          【解决方案8】:

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

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

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

          然后创建一个 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;
              }
          }
          

          对序列化这种类型的数据很有用。

          【讨论】:

            【解决方案9】:

            我将可序列化的类用于不同模块之间的 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; }
                    }
                }
            

            用法很简单 - 我为我的数据对象的字典字段 - 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},
                };
            

            【讨论】:

              【解决方案10】:

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

              http://www.sharpserializer.com/

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

              不需要用任何属性标记你的对象,也不需要在 Serialize 方法中给出对象类型(参见here)。

              通过 nuget 安装:Install-package sharpserializer

              那么就很简单了:

              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");
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2015-11-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多