Search code examples
c#.netxmlxml-deserializationobjectmapper

Deserialize and map each repeating XML node to an object's property using C# Xml.Serialization library



First of all, I'm sorry if I misunderstood the de-serializing process and the lack of English language skills.

As my questions is always flagged as not clear and unhelpful.

I will try my best to be as clear as possible.


I'm trying to de-serialize and map an XML that has repeating nodes and mapping it to an object.

The target object has 10 properties and the XML always has 10 nodes.

The XML:

<some_object>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
  <field>
    <field_name>  </field_name>
    <field_value>  </field_value>
  </field>
</some_object>

<field_name> indicate a property name of the target object.

<field_value> is the value of that property.

My current class:

public class some_object
{
  [XmlElement("field")]
  public Field[] field { get; set; }

  public class Field
  {
    public string field_name { get; set; }
    public string field_value { get; set; }
  }
}

I managed to de-serialize the XML using above class and get an object that has an array of Field objects.

But what I really wanted is to de-serialize the XML to (let's say) this below class.

Each <field> should be mapped to a property. <field_name> indicate the property name and should be map to that property. <field_value> is assigned to that property's value.

public class some_class
{
  public int property1 { get; set; }
  public DateTime property2 { get; set; }
  public string property3 { get; set; }
  public string property4 { get; set; }
  public string property5 { get; set; }
  public string property6 { get; set; }
  public string property7 { get; set; }
  public string property8 { get; set; }
  public string property9 { get; set; }
  public string property10 { get; set; }
}

Do I have to write another method to map the node to the property?

Of can I de-serialize and map the XML to the above class directly using Serialization class?

My goal is to write a generic method that can de-serialize and map any XML to any object.

I hope that make sense.

Thank you in advance for any tips and advice on proper de-serialize process in C#.

Regards


Solution

  • I can suggest the following code that will correctly populate the some_class properties from the xml provided.

    var settings = new XmlReaderSettings { IgnoreWhitespace = true };
    using var reader = XmlReader.Create("test.xml", settings);
    
    var some = new some_class();
    var properties = some.GetType().GetProperties(
        BindingFlags.Public | BindingFlags.Instance);
    
    while (reader.ReadToFollowing("field_name"))
    {
        string name = reader.ReadElementContentAsString();
        int i = Array.FindIndex(properties, p => p.Name == name);
    
        if (i != -1)
        {
            var p = properties[i];
            Type type = p.PropertyType;
            object value = null;
    
            if (type == typeof(string))
                value = reader.ReadElementContentAsString();
            else if (type == typeof(int))
                value = reader.ReadElementContentAsInt();
            else if (type == typeof(DateTime))
                value = reader.ReadElementContentAsDateTime();
            // and so on...
    
            p.SetValue(some, Convert.ChangeType(value, type));
        }
    }