Search code examples
c#xmldeserializationxml-deserializationixmlserializable

c# Deserialize with IXmlSerializable


I have xml like this:

    <data>    
  <audit_values>
    <audit_value>
      <channel>2</channel>
      <week>
        <mo_th>6,501698000000</mo_th>
        <fr>8,414278000000</fr>
        <sa>9,292674000000</sa>
        <sun>8,551982000000</sun>
        <holid>7,164605000000</holid>
      </week>
    </audit_value>
    <audit_value>
      <channel>1</channel>
      <week>
        <mo_th>6,501698000000</mo_th>
        <fr>8,414278000000</fr>
        <sa>9,292674000000</sa>
        <sun>8,551982000000</sun>
        <holid>7,164605000000</holid>
      </week>
    </audit_value>
  </audit_values>
</data>

And I need to deserialize it to class. But the problem is, that week will be change in future(it will be contains more elements, and name of them I dont know)

Data:

[XmlRoot("data")]
public class Data
{
    [XmlArray("audit_values")]
    [XmlArrayItem("audit_value", IsNullable = true)]
    public AuditValue[] AuditValues { get; set; }
}

AuditValue:

[XmlRoot("audit_value")]
public class AuditValue
{
    [XmlElement("week", typeof(TVR))]
    public Week Week;
}

Week:

    [XmlRoot("week")]
public class Week : IXmlSerializable
{
    public Dictionary<string, double> Values = new Dictionary<string, double>();

    public XmlSchema GetSchema()
    {
        return null;
    }

    public void ReadXml(XmlReader reader)
    {
        reader.Read();
        var sub = reader.ReadSubtree();
        do
        {
            if (sub.NodeType == XmlNodeType.Element)
            {
                string name = sub.Name;
                string val = sub.ReadElementContentAsString();
                Values.Add(name, Convert.ToDouble(val));
            }
        } while (sub.Read());
    }

    public void WriteXml(XmlWriter writer)
    {

    }
}

But after deserialization I have only one element with only one recored in dictionary Values. What I'm doing wrong?

GitHub: https://github.com/olegletynain/XmlTest/tree/master/XmlTestRead


Solution

  • I tweaked your ReadXml method based on @Matthew Whited's idea in the comments section and the following method does the job:

    public void ReadXml(XmlReader reader)
    {
        Values = XElement.Parse(reader.ReadOuterXml())
            .Elements()
            .ToDictionary(k => k.Name.ToString(), v => double.Parse(v.Value));
    }
    

    As a side note you only need XmlRoot on the actual root element not on every class so I removed it from AuditValue and Week. Also I don't know what TVR is. It didn't compile with "typeof(TVR)" so I removed it as well.

    For the sake of completeness here's my version of the classes:

    [XmlRoot("data")]
    public class Data
    {
        [XmlArray("audit_values")]
        [XmlArrayItem("audit_value", IsNullable = true)]
        public AuditValue[] AuditValues { get; set; }
    }
    
    public class AuditValue
    {
        [XmlElement("week")]
        public Week Week;
    }
    
    public class Week : IXmlSerializable
    {
        public Dictionary<string, double> Values = new Dictionary<string, double>();
    
        public XmlSchema GetSchema()
        {
            return null;
        }
    
        public void ReadXml(XmlReader reader)
        {
            Values = XElement.Parse(reader.ReadOuterXml())
                .Elements()
                .ToDictionary(k => k.Name.ToString(), v => double.Parse(v.Value));
        }
    
        public void WriteXml(XmlWriter writer)
        {
    
        }
    }