How do you deserialize XML with dynamic element names?

后端 未结 2 745
佛祖请我去吃肉
佛祖请我去吃肉 2020-12-04 03:53

My XML looks like follows:


    1250.00
    1900.00

This

相关标签:
2条回答
  • 2020-12-04 04:08

    You can do this by having your Rates collection implement IXmlSerializable:

    public class Rates : List<Rate>, IXmlSerializable
    {
        public Rates() : base() { }
        public Rates(IEnumerable<Rate> collection) : base(collection) { }
    
        #region IXmlSerializable Members
    
        XmlSchema IXmlSerializable.GetSchema()
        {
            return null;
        }
    
        void IXmlSerializable.ReadXml(XmlReader reader)
        {
            // for the `decodeName` delegate, you could check that the node name matches the pattern "rateN" for some integer N, if you want.
            XmlKeyValueListHelper.ReadXml(reader, this, null, s => new Rate { RateValue = s });
        }
    
        void IXmlSerializable.WriteXml(XmlWriter writer)
        {
            XmlKeyValueListHelper.WriteXml(writer, this, (i, rate) => "rate" + XmlConvert.ToString(i), r => r.RateValue);
        }
    
        #endregion
    }
    
    public class Rate
    {
        public string RateValue;
    }
    
    public static class XmlKeyValueListHelper
    {
        const string XsiNamespace = @"http://www.w3.org/2001/XMLSchema-instance";
        const string XsiNil = "nil";
    
        public static void WriteXml<T>(XmlWriter writer, IEnumerable<T> collection, Func<int, T, string> encodeName, Func<T, string> encodeValue)
        {
            int i = 0;
            foreach (var item in collection)
            {
                writer.WriteStartElement(XmlConvert.EncodeLocalName(encodeName(i, item)));
                if (item == null)
                {
                    writer.WriteAttributeString(XsiNil, XsiNamespace, XmlConvert.ToString(true));
                }
                else
                {
                    writer.WriteValue(encodeValue(item) ?? "");
                }
                writer.WriteEndElement();
                i++;
            }
        }
    
        public static void ReadXml<T>(XmlReader reader, ICollection<T> collection, Func<int, string, bool> decodeName, Func<string, T> decodeValue)
        {
            if (reader.IsEmptyElement)
            {
                reader.Read();
                return;
            }
    
            int i = 0;
            reader.ReadStartElement(); // Advance to the first sub element of the list element.
            while (reader.NodeType == XmlNodeType.Element)
            {
                var key = XmlConvert.DecodeName(reader.Name);
                if (decodeName == null || decodeName(i, key))
                {
                    var nilValue = reader[XsiNil, XsiNamespace];
                    if (!string.IsNullOrEmpty(nilValue) && XmlConvert.ToBoolean(nilValue))
                    {
                        collection.Add(default(T));
                        reader.Skip();
                    }
                    else
                    {
                        string value;
                        if (reader.IsEmptyElement)
                        {
                            value = string.Empty;
                            // Move past the end of item element
                            reader.Read();
                        }
                        else
                        {
                            // Read content and move past the end of item element
                            value = reader.ReadElementContentAsString();
                        }
                        collection.Add(decodeValue(value));
                    }
                }
                else
                {
                    reader.Skip();
                }
                i++;
            }
            // Move past the end of the list element
            reader.ReadEndElement();
        }
    }
    

    Example fiddle.

    0 讨论(0)
  • 2020-12-04 04:25

    Dynamic element names can be simple to process using the XmlSerializer.UnknownElement event handler.

    For example, we have following xml:

    <main>
      <rates>
        <rate1>1250.00</rate1>
        <rate2>1900.00</rate2>
      </rates>
    </main>
    

    and classes:

    [XmlRoot("main")]
    public class Main
    {
        public List<Rate> Rates { get; set; }
    }
    
    public class Rate
    {
        public string Value { get; set; }
    }
    

    Do deserialize:

    var xs = new XmlSerializer(typeof(Main));
    // add event handler
    xs.UnknownElement += Xs_UnknownElement;
    
    Main main;
    using (var fs = new FileStream("test.xml", FileMode.Open))
        main = (Main)xs.Deserialize(fs);
    

    The event handler code:

    private void Xs_UnknownElement(object sender, XmlElementEventArgs e)
    {
        Main main = (Main)e.ObjectBeingDeserialized;
    
        foreach (XmlNode node in e.Element.ChildNodes)
            main.Rates.Add(new Rate { Value = node.InnerText });
    }
    
    0 讨论(0)
提交回复
热议问题