Deserialize an XML array into a list using reflection

227 Views Asked by At

I want to deserialize an XML document into an object, this is my object definition:

public class Student
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public List<string> Hobbies { get; set; }
}

And this is the XML file, the node names match class properties:

<?xml version="1.0" encoding="UTF-8"?>
<items>
    <item type="dict">
        <FirstName type="str">John</FirstName>
        <LastName type="str">Smith</LastName>
        <Hobbies type="list">
            <item type="str">Reading</item>
            <item type="str">Singing</item>
            <item type="str">Tennis</item>
        </Hobbies>
    </item>
</items>

The following code used to work, I would pass XML node (item in this case) to the function and the code would use reflection to match the property with child nodes and set the property values:

public void DeserializeNode(XmlNode node)
{
    var student = new Student();

    foreach (XmlNode child in node)
    {
        PropertyInfo prop = student.GetType().GetProperty(child.Name);
        prop.SetValue(student, child.InnerText);
    }
}

But the above function does not work anymore (the XML input has changed and now it has an array called hobbies)

The following line throws exception:

prop.SetValue(student, child.InnerText); // child.InnerText = ReadingSingingTennis

This is because child.InnerText for the Hobbies return ReadingSingingTennis and the code tries to set the List<string> tp a single string.

How can modify this function to set the hobbies correctly?

1

There are 1 best solutions below

0
Svyatoslav Ryumkin On BEST ANSWER

The problem is that in Hobbies you have nodes.

So you can try like this.

 public static void DeserializeNode(XmlNode node)
    {
        var student = new Student();

        foreach (XmlNode child in node)
        {
            PropertyInfo prop = student.GetType().GetProperty(child.Name);
            if (child.Attributes.GetNamedItem("type").Value == "list")
            {
                var list = Activator.CreateInstance(prop.PropertyType);
                foreach (XmlNode item in child)
                {
                    var methodInfo = list.GetType().GetMethod("Add");
                    methodInfo.Invoke(list, new object[] { item.InnerText });
                }

                prop.SetValue(student, list);
            }
            else
            {
                prop.SetValue(student, child.InnerText);
            }
        }
    }

But if you have more complex xml you should use recursion and reflection