【问题标题】:How to XML-serialize a dictionary如何对字典进行 XML 序列化
【发布时间】:2010-09-08 19:31:32
【问题描述】:

我已经能够以这种方式序列化 IEnumerable:

[XmlArray("TRANSACTIONS")]
[XmlArrayItem("TRANSACTION", typeof(Record))]
public IEnumerable<BudgetRecord> Records
{
    get 
    {
        foreach(Record br in _budget)
        {
            yield return br;
        }
    }
}

但是,我意识到现在我需要一个包含集合 Dictionary&lt;string, RecordCollection&gt; 的字典(RecordCollection 实现了 IEnumerable)。

我怎样才能做到这一点?

【问题讨论】:

标签: c# .net xml-serialization xmlserializer


【解决方案1】:

看看下面的博文

还有这个(不是英文,但代码很有用)


代码示例来自: http://web.archive.org/web/20100703052446/http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx

using System.Collections.Generic;
using System.Collections;
using System.IO;
using System.Xml.Serialization;
using System.Xml;
using System;
public static void Serialize(TextWriter writer, IDictionary dictionary)
{
    List<Entry> entries = new List<Entry>(dictionary.Count);
    foreach (object key in dictionary.Keys)
    {
        entries.Add(new Entry(key, dictionary[key]));
    }
    XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
    serializer.Serialize(writer, entries);
}
public static void Deserialize(TextReader reader, IDictionary dictionary)
{
    dictionary.Clear();
    XmlSerializer serializer = new XmlSerializer(typeof(List<Entry>));
    List<Entry> list = (List<Entry>)serializer.Deserialize(reader);
    foreach (Entry entry in list)
    {
        dictionary[entry.Key] = entry.Value;
    }
}
public class Entry
{
    public object Key;
    public object Value;
    public Entry()
    {
    }

    public Entry(object key, object value)
    {
        Key = key;
        Value = value;
    }
}

当键和值是字符串时,它会生成如下输出。

<?xml version="1.0" encoding="utf-8"?>
<ArrayOfEntry xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
  <Entry>
    <Key xsi:type="xsd:string">MyKey</Key>
    <Value xsi:type="xsd:string">MyValue</Value>  
  </Entry>
  <Entry>    
    <Key xsi:type="xsd:string">MyOtherKey</Key>    
    <Value xsi:type="xsd:string">MyOtherValue</Value>  
  </Entry>
</ArrayOfEntry>

【讨论】:

【解决方案2】:

请尝试这种简单的替代方法:

    void Main()
    {
        var model = new Foo() 
        { 
            RealDictionary = new Dictionary<string, int> { {"A", 23}, {"B", 40} } 
        };
        model.RealDictionary.Add("C", 69);
        
        using (var writer = XmlWriter.Create("c:\\test1.xml"))
            (new XmlSerializer(typeof(Foo))).Serialize(writer, model);
    }
    
    [Serializable]
    public class Foo
    {
        [XmlArray(ElementName ="Elements")]
        [XmlArrayItem(ElementName = "Element")]
        public List<KeyValueElement> SerializableDictionary 
        { 
            get { return RealDictionary.Select(x => new KeyValueElement {Key = x.Key, Value = x.Value}).ToList(); } 
            set { RealDictionary = value.ToDictionary(x=> x.Key, x => x.Value); }
        }
    
        [XmlIgnore]
        public Dictionary<string, int> RealDictionary {get;set;}  
    }
    
    [Serializable]
    public class KeyValueElement
    {
        public string Key { get; set; }
        public int Value { get; set; }  
    }

这里是xml结果:

    <Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <Elements>
            <Element>
                <Key>A</Key><Value>23</Value>
            </Element>
            <Element>
                <Key>B</Key><Value>40</Value>
            </Element>
            <Element>
                <Key>C</Key><Value>69</Value>
            </Element>
        </Elements>
    </Foo>

【讨论】:

    【解决方案3】:

    我已经使用了一段时间。最初来自here

    namespace SerializeDictionary
    {
        using System;
        using System.Collections.Generic;
        using System.Runtime.Serialization;
        using System.Xml;
        using System.Xml.Schema;
        using System.Xml.Serialization;
    
        /// <summary>
        /// Represents an XML serializable collection of keys and values.
        /// </summary>
        /// <typeparam name="TKey">The type of the keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of the values in the dictionary.</typeparam>
        [Serializable]
        [XmlRoot("dictionary")]
        public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
        {
            /// <summary>
            /// The default XML tag name for an item.
            /// </summary>
            private const string DefaultItemTag = "item";
    
            /// <summary>
            /// The default XML tag name for a key.
            /// </summary>
            private const string DefaultKeyTag = "key";
    
            /// <summary>
            /// The default XML tag name for a value.
            /// </summary>
            private const string DefaultValueTag = "value";
    
            /// <summary>
            /// The XML serializer for the key type.
            /// </summary>
            private static readonly XmlSerializer keySerializer = new XmlSerializer(typeof(TKey));
    
            /// <summary>
            /// The XML serializer for the value type.
            /// </summary>
            private static readonly XmlSerializer valueSerializer = new XmlSerializer(typeof(TValue));
    
            /// <summary>
            /// Initializes a new instance of the
            /// <see cref="SerializableDictionary&lt;TKey, TValue&gt;"/> class.
            /// </summary>
            public SerializableDictionary()
            {
            }
    
            /// <summary>
            /// Initializes a new instance of the
            /// <see cref="SerializableDictionary&lt;TKey, TValue&gt;"/> class.
            /// </summary>
            /// <param name="info">A
            /// <see cref="T:System.Runtime.Serialization.SerializationInfo"/> object
            /// containing the information required to serialize the
            /// <see cref="T:System.Collections.Generic.Dictionary`2"/>.
            /// </param>
            /// <param name="context">A
            /// <see cref="T:System.Runtime.Serialization.StreamingContext"/> structure
            /// containing the source and destination of the serialized stream
            /// associated with the
            /// <see cref="T:System.Collections.Generic.Dictionary`2"/>.
            /// </param>
            protected SerializableDictionary(SerializationInfo info, StreamingContext context) : base(info, context)
            {
            }
    
            /// <summary>
            /// Gets the XML tag name for an item.
            /// </summary>
            protected virtual string ItemTagName
            {
                get
                {
                    return DefaultItemTag;
                }
            }
    
            /// <summary>
            /// Gets the XML tag name for a key.
            /// </summary>
            protected virtual string KeyTagName
            {
                get
                {
                    return DefaultKeyTag;
                }
            }
    
            /// <summary>
            /// Gets the XML tag name for a value.
            /// </summary>
            protected virtual string ValueTagName
            {
                get
                {
                    return DefaultValueTag;
                }
            }
    
            /// <summary>
            /// Gets the XML schema for the XML serialization.
            /// </summary>
            /// <returns>An XML schema for the serialized object.</returns>
            public XmlSchema GetSchema()
            {
                return null;
            }
    
            /// <summary>
            /// Deserializes the object from XML.
            /// </summary>
            /// <param name="reader">The XML representation of the object.</param>
            public void ReadXml(XmlReader reader)
            {
                var wasEmpty = reader.IsEmptyElement;
    
                reader.Read();
                if (wasEmpty)
                {
                    return;
                }
    
                try
                {
                    while (reader.NodeType != XmlNodeType.EndElement)
                    {
                        this.ReadItem(reader);
                        reader.MoveToContent();
                    }
                }
                finally
                {
                    reader.ReadEndElement();
                }
            }
    
            /// <summary>
            /// Serializes this instance to XML.
            /// </summary>
            /// <param name="writer">The XML writer to serialize to.</param>
            public void WriteXml(XmlWriter writer)
            {
                foreach (var keyValuePair in this)
                {
                    this.WriteItem(writer, keyValuePair);
                }
            }
    
            /// <summary>
            /// Deserializes the dictionary item.
            /// </summary>
            /// <param name="reader">The XML representation of the object.</param>
            private void ReadItem(XmlReader reader)
            {
                reader.ReadStartElement(this.ItemTagName);
                try
                {
                    this.Add(this.ReadKey(reader), this.ReadValue(reader));
                }
                finally
                {
                    reader.ReadEndElement();
                }
            }
    
            /// <summary>
            /// Deserializes the dictionary item's key.
            /// </summary>
            /// <param name="reader">The XML representation of the object.</param>
            /// <returns>The dictionary item's key.</returns>
            private TKey ReadKey(XmlReader reader)
            {
                reader.ReadStartElement(this.KeyTagName);
                try
                {
                    return (TKey)keySerializer.Deserialize(reader);
                }
                finally
                {
                    reader.ReadEndElement();
                }
            }
    
            /// <summary>
            /// Deserializes the dictionary item's value.
            /// </summary>
            /// <param name="reader">The XML representation of the object.</param>
            /// <returns>The dictionary item's value.</returns>
            private TValue ReadValue(XmlReader reader)
            {
                reader.ReadStartElement(this.ValueTagName);
                try
                {
                    return (TValue)valueSerializer.Deserialize(reader);
                }
                finally
                {
                    reader.ReadEndElement();
                }
            }
    
            /// <summary>
            /// Serializes the dictionary item.
            /// </summary>
            /// <param name="writer">The XML writer to serialize to.</param>
            /// <param name="keyValuePair">The key/value pair.</param>
            private void WriteItem(XmlWriter writer, KeyValuePair<TKey, TValue> keyValuePair)
            {
                writer.WriteStartElement(this.ItemTagName);
                try
                {
                    this.WriteKey(writer, keyValuePair.Key);
                    this.WriteValue(writer, keyValuePair.Value);
                }
                finally
                {
                    writer.WriteEndElement();
                }
            }
    
            /// <summary>
            /// Serializes the dictionary item's key.
            /// </summary>
            /// <param name="writer">The XML writer to serialize to.</param>
            /// <param name="key">The dictionary item's key.</param>
            private void WriteKey(XmlWriter writer, TKey key)
            {
                writer.WriteStartElement(this.KeyTagName);
                try
                {
                    keySerializer.Serialize(writer, key);
                }
                finally
                {
                    writer.WriteEndElement();
                }
            }
    
            /// <summary>
            /// Serializes the dictionary item's value.
            /// </summary>
            /// <param name="writer">The XML writer to serialize to.</param>
            /// <param name="value">The dictionary item's value.</param>
            private void WriteValue(XmlWriter writer, TValue value)
            {
                writer.WriteStartElement(this.ValueTagName);
                try
                {
                    valueSerializer.Serialize(writer, value);
                }
                finally
                {
                    writer.WriteEndElement();
                }
            }
        }
    }
    

    【讨论】:

      【解决方案4】:

      这是基于 Gildors 答案的更短版本:

      [XmlElement("Dictionary")]
      public List<KeyValuePair<string, string>> XMLDictionaryProxy
      {
          get
          {
              return new List<KeyValuePair<string, string>>(this.Dictionary);
          }
          set
          {
              this.Dictionary = new Dictionary<string, string>();
              foreach (var pair in value)
                  this.Dictionary[pair.Key] = pair.Value;
          }
      }
      
      [XmlIgnore]
      public Dictionary<string, string> Dictionary
      {
          get; set;
      }
      

      享受吧。

      【讨论】:

      • 不起作用 - KeyValuePair 不能使用 XmlSerializable 序列化,因为 KeyValue 都是只读的。可以找到一个可能的解决方法here
      【解决方案5】:

      如果你正在学习C#,你不妨用你想要的逻辑创建一个类。在此示例中,我创建了一个 ProgressiveTax 对象,您可以调用 .Evaluate() 来计算税收。

      您还可以写入或读取XML 字符串(可以写入文件)

      例如,首先从提供的PAYE 信息中填充税级并保存到文件PAYE.xml。然后忘记税级(超出{ } 的范围)。然后读取文件以从文件中填充税表

      static class Program
      {
          static void Main(string[] args)
          {
              {   
                  // create a tax table and save it to a file
                  var tax = ProgressiveTax.PAYE();
                  File.WriteAllText("PAYE.xml", tax.ToXml());
              }
              {   
                  // read a tax table from a file
                  var tax = ProgressiveTax.FromXml(File.ReadAllText("PAYE.xml"));
      
                  // use the tax table
                  var x = tax.Evaluate(42250m);
                  Debug.WriteLine($"Tax={x}");
              }            
          }
      }
      

      xml文件长这样,可以手动编辑,也可以从数据库/webservice生成。

      <?xml version="1.0" encoding="utf-16"?>
      <ProgressiveTax xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" Credit="2400">
        <Brackets>
          <Bracket>
            <Item1>0</Item1>
            <Item2>0.1</Item2>
          </Bracket>
          <Bracket>
            <Item1>24000</Item1>
            <Item2>0.15</Item2>
          </Bracket>
          <Bracket>
            <Item1>40667</Item1>
            <Item2>0.2</Item2>
          </Bracket>
          <Bracket>
            <Item1>57334</Item1>
            <Item2>0.25</Item2>
          </Bracket>
        </Brackets>
      </ProgressiveTax>
      

      以及实际持有税务信息并计算税额的类

      public class ProgressiveTax
      {
          public ProgressiveTax()
          {
              this.Table = new SortedDictionary<decimal, float>();
          }
          public ProgressiveTax(SortedDictionary<decimal, float> table)
          {
              this.Table = table;
          }
      
          public static ProgressiveTax PAYE()
          {
              var tax = new ProgressiveTax();
              tax.Credit = 2400m;
              tax.Table[0m] = 0.1f;
              tax.Table[24000m] = 0.15f;
              tax.Table[40667m] = 0.20f;
              tax.Table[57334m] = 0.25f;
              return tax;
          }
      
          public string ToXml()
          {
              var fs = new StringWriter();
              var xs = new XmlSerializer(typeof(ProgressiveTax));
              xs.Serialize(fs, this);
              fs.Close();
              return fs.ToString();
          }
      
          public static ProgressiveTax FromXml(string xml)
          {
              var fs = new StringReader(xml);
              var xs = new XmlSerializer(typeof(ProgressiveTax));
              var tax = xs.Deserialize(fs) as ProgressiveTax;
              fs.Close();
              return tax;
          }
      
          [XmlAttribute]
          public decimal Credit { get; set; }
        
          [XmlIgnore()]  
          SortedDictionary<decimal, float> Table { get; }
      
          [XmlArrayItem(ElementName = "Bracket")]
          public (decimal lower, float rate)[] Brackets
          {
              get
              {
                  var parts = new (decimal lower, float rate)[Table.Count];
                  int index = 0;
                  foreach (var item in Table)
                  {
                      parts[index++] = (item.Key, item.Value);
                  }
                  return parts;
              }
              set
              {
                  Table.Clear();
                  foreach (var (lower, rate) in value)
                  {
                      Table[lower] = rate;
                  }
              }
          }
      
          public decimal Evaluate(decimal income)
          {
              decimal result = -Credit;
              foreach (var item in Table.Reverse())
              {
                  if (item.Key <= income)
                  {
                      Debug.WriteLine($"Assess {item.Value:P2} tax on {income - item.Key}");
                      result += (decimal)( item.Value * (float) (income - item.Key));
                      income = item.Key;
                  }
              }
              return Math.Max(0m, result);
          }
      }
      

      示例程序在调试器中产生以下输出。

      Assess 20.00% tax on 1583
      Assess 15.00% tax on 16667
      Assess 10.00% tax on 24000
      Tax=2816.65
      

      如果把1583 + 16667 + 24000 = 42250加起来就是总收入。由于这是累进税,因此使用上述税率和金额,然后记入 2400。也不是结果必须为 0 或正数。

      【讨论】:

        【解决方案6】:

        这个SerializableDictionary 类可以像普通的Dictionary 一样使用,也可以是Serialize()

        Gist

        SerializableDictionary.cs

        using System;
        using System.Collections.Generic;
        using System.Xml;
        using System.Xml.Schema;
        using System.Xml.Serialization;
        
        /// <summary>
        /// Base on https://weblogs.asp.net/pwelter34/444961
        /// </summary>
        /// <typeparam name="TKey"></typeparam>
        /// <typeparam name="TValue"></typeparam>
        [XmlRoot("dictionary")]
        public class SerializableDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IXmlSerializable
        {
            // XmlSerializer.Deserialize() will create a new Object, and then call ReadXml()
            // So cannot use instance field, use class field.
        
            public static string itemTag = "item";
            public static string keyTag = "key";
            public static string valueTag = "value";
        
            public XmlSchema GetSchema()
            {
                return null;
            }
        
            public void ReadXml(XmlReader reader)
            {
                if (reader.IsEmptyElement)
                    return;
        
                var keySerializer = new XmlSerializer(typeof(TKey));
                var valueSerializer = new XmlSerializer(typeof(TValue));
        
                reader.ReadStartElement();
        
                // IsStartElement() will call MoveToContent()
                // reader.MoveToContent();
                while (reader.IsStartElement(itemTag))
                {
                    reader.ReadStartElement(itemTag);
        
                    reader.ReadStartElement(keyTag);
                    TKey key = (TKey)keySerializer.Deserialize(reader);
                    reader.ReadEndElement();
        
                    reader.ReadStartElement(valueTag);
                    TValue value = (TValue)valueSerializer.Deserialize(reader);
                    reader.ReadEndElement();
        
                    reader.ReadEndElement();
                    this.Add(key, value);
        
                    // IsStartElement() will call MoveToContent()
                    // reader.MoveToContent();
                }
                reader.ReadEndElement();
            }
        
            public void WriteXml(XmlWriter writer)
            {
                var keySerializer = new XmlSerializer(typeof(TKey));
                var valueSerializer = new XmlSerializer(typeof(TValue));
        
                foreach (var kvp in this)
                {
                    writer.WriteStartElement(itemTag);
        
                    writer.WriteStartElement(keyTag);
                    keySerializer.Serialize(writer, kvp.Key);
                    writer.WriteEndElement();
        
                    writer.WriteStartElement(valueTag);
                    valueSerializer.Serialize(writer, kvp.Value);
                    writer.WriteEndElement();
        
                    writer.WriteEndElement();
                }
            }
        }
        

        Demo.cs

        using System;
        using System.IO;
        using System.Xml.Serialization;
        
        //[XmlRoot("AnimalDictionary")]
        public class SDict : SerializableDictionary<string, string>
        {
        }
        
        namespace Test
        {
            public class Program
            {
        
                static void Main(string[] args)
                {
                    var dict = new SDict();
                    //SDict.itemTag = "AnimalCount";
                    //SDict.keyTag = "Type";
                    //SDict.valueTag = "Count";
                    dict.Add("Cat", "1");
                    dict.Add("Dog", "2");
        
                    foreach (var kvp in dict)
                        Console.WriteLine(kvp);
        
                    XmlSerializer serializer = new XmlSerializer(typeof(SDict));
        
                    using (var writer = new StreamWriter("dict.xml"))
                    {
                        serializer.Serialize(writer, dict);
                    }
        
                    Console.WriteLine("\nXML File:");
                    Console.WriteLine(File.ReadAllText("dict.xml"));
        
                    using (var reader = new StreamReader("dict.xml"))
                    {
                        dict = serializer.Deserialize(reader) as SDict;
                    }
        
                    Console.WriteLine("\nAfter Deserialize");
        
                    foreach (var kvp in dict)
                        Console.WriteLine(kvp);
        
                    Console.ReadLine();
                }
            }
        }
        

        【讨论】:

          猜你喜欢
          • 2020-03-04
          • 1970-01-01
          • 2010-09-09
          • 2018-09-27
          相关资源
          最近更新 更多