3

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

Oleg L
  • 161
  • 1
  • 14
  • Possible duplicate of [Deserialize random/unknown types with XmlSerializer](http://stackoverflow.com/questions/8210386/deserialize-random-unknown-types-with-xmlserializer) – GSerg Oct 15 '15 at 16:58
  • Sorry, but I have a problem in implementation of IXmlSerializable and there not unknown types. It's not duplicate – Oleg L Oct 15 '15 at 17:07
  • Have you tried adding a breakpoint and debugging your code? – Glenn Ferrie Oct 15 '15 at 17:09
  • Yes, I think that problem is in `ReadXml` but I cannot understand where. – Oleg L Oct 15 '15 at 17:11
  • GitHub test: https://github.com/olegletynain/XmlTest/tree/master/XmlTestRead – Oleg L Oct 15 '15 at 17:30
  • Check your reader... is sub on the correct element? I've done this in the last is to write hand code the deserializer in LINQ to XML. So much nicer than `System.Xml` – Matthew Whited Oct 15 '15 at 17:52
  • If after `reader.Read()` you are on `week`.. I'd pass the reader to `XElement.Read(reader) as XElement` then just `.Elements().ToDictionary(...)` – Matthew Whited Oct 15 '15 at 17:58

4 Answers4

2

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

    }
}
Volkan Paksoy
  • 6,727
  • 5
  • 29
  • 40
1

You should consider using the DataContractSerializer instead of XmlSerializer and implement the IExtensibleDataObject interface on your DataContract class.

Implementing IExtensibleDataObject allows the DataContract class to persist unknown information in the ExtensionData field, which prevents it from being lost if the XML being deserialized contains unknown elements, and then is re-serialized and saved.

Your Audit class would look something like this:

[DataContract(Name = "audit_value", Namespace = "")]
public class AuditValue : IExtensibleDataObject
{
    [DataMember(Name = "channel")]
    public int Channel { get; set; }

    [DataMember(Name = "week")]
    public Week Week { get; set; }

    public ExtensionDataObject ExtensionData { get; set; }
}

[DataContract(Name = "week", Namespace = "")]
public class Week : IExtensibleDataObject
{
    [DataMember(Name = "mo_th")]
    public Decimal MondayThroughThursday { get; set; }

    public ExtensionDataObject ExtensionData { get; set; }
}

You'll still need to update your code to deserialize the additional elements as POCOs, but at least the underlying data will be persisted until you get around to it.

DVK
  • 2,726
  • 1
  • 17
  • 20
0

Try this. XmlElement eliminates a lay of tags which you don't have. You have an extra 's' at end of values.

From 
    [XmlArray("audit_values")]
    [XmlArrayItem("audit_value", IsNullable = true)]
    public AuditValue[] AuditValues { get; set; }
​To
    [XmlElement("audit_value")]
    public AuditValue[] AuditValues { get; set; }
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • You don't need two levels of tags and just . Using XlEement instead of XmlArray gets rid of unnecessary tag. – jdweng Oct 15 '15 at 17:35
  • What I said is correct. You may have a requirement to add the extra level of tags but the extra tag is not required. – jdweng Oct 16 '15 at 13:57
0

The error was in ReadXml method, now I changed it to this:

    public void ReadXml(XmlReader reader)
    {
        reader.Read();
        do
        {
            if (!reader.IsEmptyElement)
            {
                var name = reader.Name;
                var val = Convert.ToDouble(reader.ReadElementContentAsString());
                Values.Add(name, val);
            }
            else
            {
                reader.Skip();
            }
        } while (reader.Name != "week");
        if (reader.NodeType == XmlNodeType.EndElement)
        {
            reader.ReadEndElement();
        }
    }

And I works fine. This Solution without using XElement and Linq, @Volkan Paksoy offered method with XElement which is easier to understand

Oleg L
  • 161
  • 1
  • 14