Search code examples
c#xmlgenericsxml-parsingxml-deserialization

Convert XML string to List<T> without specifiying Element Root in C#


I need to convert the XML string to List, the method should be generic. I wrote a method but its not performing as expected.

Scenario: #1

Model Class:

public class Employee {
    public int EmpId { get; set; }
    public string Name { get; set; }
}

XML:

<EmployeeList
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Employee>
        <EmpId>1</EmpId>
        <Name>Emma</Name>
    </Employee>
    <Employee>
        <EmpId>2</EmpId>
        <Name>Watson</Name>
    </Employee>
</EmployeeList>

Scenario: #2

Model Class:

public class Person {
    public int PersonId { get; set; }
    public string Name { get; set; }
}

XML:

<PersonList
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <Person>
        <PersonId>1</EmpId>
        <Name>Emma</Name>
    </Person>
    <Person>
        <PersonId>2</EmpId>
        <Name>Watson</Name>
    </Person>
</PersonList>

I need a generic method to Convert the above said XML's to List<Employee> and List<Person>.

I used the following code

public static T[] ParseXML<T>(this string @this) where T : class {
    var reader = XmlReader.Create(@this.Trim().ToStream(),new XmlReaderSettings() { ConformanceLevel = ConformanceLevel.Document });
    return new XmlSerializer(typeof(T[])).Deserialize(reader) as T[];
}

But I'm getting NULL. Kindly assist me how to handle this.

I refereed lots of code but they are telling to specify the Root element as hard-coded value. But I need a generic method.

The Signature should be

public static T[] ParseXML<T>(this string @this) where T : class { }

Kindly assist me in this regards.


Solution

  • The default root name is ArrayOfThing, not ThingList, so you will need to tell the serializer:

    var ser = new XmlSerializer(list.GetType(), new XmlRootAttribute("EmployeeList"));
    

    However, you'll also need to cache and re-use this to prevent assembly memory leaks (only the most basic constructors automatically cache). A static read-only field on a generic type is a good bet, for example:

    static class SerializerCache<T> {
       public static readonly XmlSerializer Instance = new XmlSerializer(
           typeof(List<T>), new XmlRootAttribute(typeof(T).Name + "List"));
    }
    

    then use SerializerCache<T>.Instance instead of new XmlSerializer

    Obviously swap lists and arrays if you like...