How to XML-serialize a dictionary

前端 未结 5 492
执笔经年
执笔经年 2020-11-29 07:16

I have been able to serialize an IEnumerable this way:

[XmlArray(\"TRANSACTIONS\")]
[XmlArrayItem(\"TRANSACTION\", typeof(Record))]
public IEnumerable

        
相关标签:
5条回答
  • 2020-11-29 07:54

    I have used the below for some time. It originally comes from 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();
                }
            }
        }
    }
    
    0 讨论(0)
  • 2020-11-29 07:57

    If you are learning C# you might as well create a class with the logic you want. In this example I made a ProgressiveTax object that you can call .Evaluate() with to calculate taxes.

    You can also write or read from XML strings (which can be written to files)

    For example, first populate the tax brackets from PAYE information provided and save to a file PAYE.xml. Then forget the tax brackets (out of scope from { }). And then read the file to populate the tax table from a file

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

    The xml file looks like this, which can be edited manually, or be generated from a database/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>
    

    and the class that actually holds the tax information and calculates the tax amount

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

    The sample program produces the following output in the debugger.

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

    If you add up 1583 + 16667 + 24000 = 42250 which the total amount of income. Since this is a progressive tax, the above rates and amounts are used, and then 2400 is credited. Also not the result must be 0 or positive.

    0 讨论(0)
  • 2020-11-29 08:11

    Here's an even shorter version based on Gildors answer:

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

    Enjoy.

    0 讨论(0)
  • 2020-11-29 08:12

    Take a look at the following blog post

    • http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx
    • http://web.archive.org/web/20100703052446/http://blogs.msdn.com/b/psheill/archive/2005/04/09/406823.aspx

    and this one (not in english, but the code is useful)

    • http://huseyint.com/2007/12/xml-serializable-generic-dictionary-tipi/

    Code sample from: 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;
        }
    }
    

    It generates output like the following, when the keys and values are strings.

    <?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>
    
    0 讨论(0)
  • 2020-11-29 08:20

    Please try this alternative easy way:

    void Main()
    {
        var source=
            new TestClass() 
            { 
                GroupTestTyped=
                    new Dictionary<string, int> { {"A", 23}, {"B", 40} }
            };
        using (var writer = XmlWriter.Create("c:\\test1.xml"))
            (new XmlSerializer(typeof(TestClass))).Serialize(writer, source);
    }
    [Serializable]
    public class DemoElementClass
    {
        public string Key { get; set; }
        public int Value { get; set; }  
    }
    [Serializable]
    public class TestClass
    {
       public TestClass() { }
    
       [XmlArray]
       [XmlArrayItem(ElementName = "ElementTest")]
       public List<DemoElementClass> GroupTest { get; set; }
    
       [XmlIgnore]
       public Dictionary<string, int> GroupTestTyped 
       {
           get { return GroupTest.ToDictionary(x=> x.Key, x => x.Value); }
           set { GroupTest = value.Select(x => new DemoElementClass {Key = x.Key, Value = x.Value}).ToList(); }
       }
    }
    

    Here the xml result:

    <?xml version="1.0" encoding="utf-8"?>
    <TestClass xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <GroupTest>
            <ElementTest>
                <Key>A</Key>
                <Value>23</Value>
            </ElementTest>
            <ElementTest>
                <Key>B</Key>
                <Value>40</Value>
            </ElementTest>
        </GroupTest>
    </TestClass>
    
    0 讨论(0)
提交回复
热议问题