Search code examples
c#xmldeserializationxml-deserialization

Deserialize XML elements


I have some problems deserializing my XML into class.

This is the XML:

<?xml version="1.0" encoding="utf-8" ?>
<InterestRates>

  <!--Type A -->
  <InterestRates_A>
    <InterestRate_A>
      <ValidFrom></ValidFrom>
      <ValidTo>2004-12-31</ValidTo>
      <Rate>0.00000</Rate>
    </InterestRate_A>
    <InterestRate_A>
      <ValidFrom>2005-01-01</ValidFrom>
      <ValidTo>2005-12-31</ValidTo>
      <Rate>0.04247</Rate>
    </InterestRate_A>
    <InterestRate_A>
      <ValidFrom>2005-01-01</ValidFrom>
      <ValidTo>2005-12-31</ValidTo>
      <Rate>0.04247</Rate>
    </InterestRate_A>
    <InterestRate_A>
      <ValidFrom>2006-01-01</ValidFrom>
      <ValidTo>2006-12-31</ValidTo>
      <Rate>0.02986</Rate>
    </InterestRate_A>
    <InterestRate_A>
      <ValidFrom>2007-01-01</ValidFrom>
      <ValidTo>2009-10-30</ValidTo>
      <Rate>0.02740</Rate>
    </InterestRate_A>
    <InterestRate_A>
      <ValidFrom>2009-10-31</ValidFrom>
      <ValidTo>2009-10-30</ValidTo>
      <Rate>0.02470</Rate>
    </InterestRate_A>
  </InterestRates_A>

  <!--Type B -->
  <InterestRates_B>
    <InterestRate_B>
      <ValidFrom>2016-05-01</ValidFrom>
      <ValidTo></ValidTo>
      <Rate>0.05</Rate>
      <Rate2>2.05</Rate2>
    </InterestRate_B>
  </InterestRates_B>

  <!--Type C -->
  <InterestRates_C>
    <InterestRate_C>
      <ValidFrom>2017-01-01</ValidFrom>
      <ValidTo></ValidTo>
      <Rate>2</Rate>
    </InterestRate_C>
  </InterestRates_C>

  <!--Type D -->
  <InterestRates_D>
    <InterestRate_D>
      <ValidFrom>2017-01-01</ValidFrom>
      <ValidTo></ValidTo>
      <Rate>3</Rate>
    </InterestRate_D>
  </InterestRates_D>

  <!--Type E -->
  <InterestRates_E>
    <InterestRate_E>
      <ValidFrom>2017-01-01</ValidFrom>
      <ValidTo></ValidTo>
      <Rate>5</Rate>
    </InterestRate_E>
  </InterestRates_E>

  <!--Type F -->
  <InterestRates_F>
    <InterestRate_F>
      <ValidFrom>2017-01-01</ValidFrom>
      <ValidTo></ValidTo>
      <Rate>7</Rate>
    </InterestRate_F>
  </InterestRates_F>

</InterestRates>

This is the code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace Interest
{
  // Root class
  [XmlRoot("InterestRates")]
  public class InterestRates
  {
      [XmlElement("InterestRates_A")]
      public InterestRates_A InterestRates_A { get; set; }

      [XmlElement("InterestRates_B")]
      public InterestRates_B InterestRates_B { get; set; }

      [XmlElement("InterestRates_C")]
      public InterestRates_C InterestRates_C { get; set; }

      [XmlElement("InterestRates_D")]
      public InterestRates_D InterestRates_D { get; set; }

      [XmlElement("InterestRates_E")]
      public InterestRates_E InterestRates_E { get; set; }

      [XmlElement("InterestRates_F")]
      public InterestRates_F InterestRates_F { get; set; }
  }


  [XmlType("InterestRates_A")]
  public class InterestRates_A
  {
    [XmlArrayItem("InterestRate_A", typeof(InterestRate_A))]
    public InterestRate_A[] InterestRate_A { get; set; }
  }

  [XmlType("InterestRates_B")]
  public class InterestRates_B
  {
    [XmlArray("InterestRate_B")]
    [XmlArrayItem("InterestRate_B", typeof(InterestRate_B))]
    public InterestRate_B[] InterestRate_B { get; set; }
  }

  [XmlType("InterestRates_C")]
  public class InterestRates_C
  {
    [XmlArray("InterestRate_C")]
    [XmlArrayItem("InterestRate_C", typeof(InterestRate_C))]
    public InterestRate_C[] InterestRate_C { get; set; }
  }

  [XmlType("InterestRates_D")]
  public class InterestRates_D
  {
    [XmlArray("InterestRate_D")]
    [XmlArrayItem("InterestRate_D", typeof(InterestRate_D))]
    public InterestRate_D[] InterestRate_D { get; set; }
  }

  [XmlType("InterestRates_E")]
  public class InterestRates_E
  {
    [XmlArray("InterestRate_E")]
    [XmlArrayItem("InterestRate_E", typeof(InterestRate_E))]
    public InterestRate_E[] InterestRate_E { get; set; }
  }

  [XmlType("InterestRates_F")]
  public class InterestRates_F
  {
    [XmlArray("InterestRate_F")]
    [XmlArrayItem("InterestRate_F", typeof(InterestRate_F))]
    public InterestRate_F[] InterestRate_F { get; set; }
  }


  [Serializable]
  public class InterestRate_A
  {
    [XmlElement("ValidFrom")]
    public string ValidFrom { get; set; }

   [XmlElement("ValidTo")]
    public string ValidTo { get; set; }

    [XmlElement("Rate")]
    public string Rate { get; set; }
  }

  [Serializable]
  public class InterestRate_B
  {
    [XmlElement("ValidFrom")]
    public string ValidFrom { get; set; }

    [XmlElement("ValidTo")]
    public string ValidTo { get; set; }

    [XmlElement("Rate")]
    public string Rate { get; set; }

    [XmlElement("Rate2")]
    public string Rate2 { get; set; }
  }

  [Serializable]
  public class InterestRate_C
  {
    [XmlElement("ValidFrom")]
    public string ValidFrom { get; set; }

    [XmlElement("ValidTo")]
    public string ValidTo { get; set; }

    [XmlElement("Rate")]
    public string Rate { get; set; }
  }

  [Serializable]
  public class InterestRate_D
  {
    [XmlElement("ValidFrom")]
    public string ValidFrom { get; set; }

    [XmlElement("ValidTo")]
    public string ValidTo { get; set; }

    [XmlElement("Rate")]
    public string Rate { get; set; }
  }

  [Serializable]
  public class InterestRate_E
  {
    [XmlElement("ValidFrom")]
    public string ValidFrom { get; set; }

    [XmlElement("ValidTo")]
    public string ValidTo { get; set; }

    [XmlElement("Rate")]
    public string Rate { get; set; }
  }

  [Serializable]
  public class InterestRate_F
  {
    [XmlElement("ValidFrom")]
    public string ValidFrom { get; set; }

    [XmlElement("ValidTo")]
    public string ValidTo { get; set; }

    [XmlElement("Rate")]
    public string Rate { get; set; }
  }
}

When I deserialize the XML into my root class InterestRates, all the InterestRate_X collections are empty. How can this be fixed?


Solution

  • The reason deserialization does not work is that you have marked the interest rate arrays in your InterestRates_X types with [XmlArray], e.g.:

      [XmlType("InterestRates_B")]
      public class InterestRates_B
      {
        [XmlArray("InterestRate_B")]
        [XmlArrayItem("InterestRate_B", typeof(InterestRate_B))]
        public InterestRate_B[] InterestRate_B { get; set; }
      }
    

    This indicates that the InterestRate_X collections are to be serialized within a container element, like so:

    <InterestRates>
      <InterestRates_B>
        <InterestRate_B> <!--The extra [XmlArray] container element -->
          <InterestRate_B>
            <ValidFrom>2016-05-01</ValidFrom>
            <ValidTo></ValidTo>
            <Rate>0.05</Rate>
            <Rate2>2.05</Rate2>
          </InterestRate_B>
        </InterestRate_B>
    

    Since your actual XML does not have this extra level of nesting, deserialization fails.

    The solution is to use [XmlElement]:

    [XmlType("InterestRates_A")]
    public class InterestRates_A
    {
        [XmlElement("InterestRate_A")]
        public InterestRate_A[] InterestRate_A { get; set; }
    }
    
    [XmlType("InterestRates_B")]
    public class InterestRates_B
    {
        [XmlElement("InterestRate_B")]
        public InterestRate_B[] InterestRate_B { get; set; }
    }
    
    [XmlType("InterestRates_C")]
    public class InterestRates_C
    {
        [XmlElement("InterestRate_C")]
        public InterestRate_C[] InterestRate_C { get; set; }
    }
    
    [XmlType("InterestRates_D")]
    public class InterestRates_D
    {
        [XmlElement("InterestRate_D")]
        public InterestRate_D[] InterestRate_D { get; set; }
    }
    
    [XmlType("InterestRates_E")]
    public class InterestRates_E
    {
        [XmlElement("InterestRate_E")]
        public InterestRate_E[] InterestRate_E { get; set; }
    }
    
    [XmlType("InterestRates_F")]
    public class InterestRates_F
    {
        [XmlElement("InterestRate_F")]
        public InterestRate_F[] InterestRate_F { get; set; }
    }
    

    Sample fiddle.

    Alternatively, you could completely eliminate your intermediate InterestRates_X types and merge all your InterestRate_X types into a single type, thereby simplifying your data model as follows:

    [XmlRoot("InterestRates")]
    public class InterestRates
    {
        [XmlArray("InterestRates_A")]
        [XmlArrayItem("InterestRate_A")]
        public InterestRate [] InterestRates_A { get; set; }
    
        [XmlArray("InterestRates_B")]
        [XmlArrayItem("InterestRate_B")]
        public InterestRate [] InterestRates_B { get; set; }
    
        [XmlArray("InterestRates_C")]
        [XmlArrayItem("InterestRate_C")]
        public InterestRate [] InterestRates_C { get; set; }
    
        [XmlArray("InterestRates_D")]
        [XmlArrayItem("InterestRate_D")]
        public InterestRate [] InterestRates_D { get; set; }
    
        [XmlArray("InterestRates_E")]
        [XmlArrayItem("InterestRate_E")]
        public InterestRate [] InterestRates_E { get; set; }
    
        [XmlArray("InterestRates_F")]
        [XmlArrayItem("InterestRate_F")]
        public InterestRate [] InterestRates_F { get; set; }
    }
    
    public class InterestRate
    {
        [XmlElement("ValidFrom")]
        public string ValidFrom { get; set; }
    
        [XmlElement("ValidTo")]
        public string ValidTo { get; set; }
    
        [XmlElement("Rate")]
        public string Rate { get; set; }
    
        [XmlElement("Rate2")]
        public string Rate2 { get; set; }
    }
    

    Sample fiddle #2.