Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XML deserialization generic method

I have next XML file:

<Root>
    <Document>      
        <Id>d639a54f-baca-11e1-8067-001fd09b1dfd</Id>
        <Balance>-24145</Balance>
    </Document>
    <Document>      
        <Id>e3b3b4cd-bb8e-11e1-8067-001fd09b1dfd</Id>
        <Balance>0.28</Balance> 
    </Document>
</Root>

I deserialize it to this class:

[XmlRoot("Root", IsNullable = false)]
public class DocBalanceCollection
{
    [XmlElement("Document")]
    public List<DocBalanceItem> DocsBalanceItems = new List<DocBalanceItem>();
}

where DocBalanceItem is:

public class DocBalanceItem
{
    [XmlElement("Id")]
    public Guid DocId { get; set; }

    [XmlElement("Balance")]
    public decimal? BalanceAmount { get; set; }
}

Here is my deserialization method:

public DocBalanceCollection DeserializeDocBalances(string filePath)
{
    var docBalanceCollection = new DocBalanceCollection();

    if (File.Exists(filePath))
    {
        var serializer = new XmlSerializer(docBalanceCollection.GetType());
        TextReader reader = new StreamReader(filePath);
        docBalanceCollection = (DocBalanceCollection)serializer.Deserialize(reader);
        reader.Close();
    }

    return docBalanceCollection;
}

All works fine but I have many XML files. Besides writing Item classes I have to write ItemCollection classes for each of them. And also I have to implement DeserializeItems method for each.

Can I deserialize my XML files without creating ItemCollection classes? And can I write single generic method to deserialize all of them?

The only solution that comes to mind - make an interface for all these classes. Any ideas?

like image 524
algreat Avatar asked Jan 28 '13 12:01

algreat


1 Answers

You can deserialize a generic List<T> just fine with XmlSerializer. However, first you need to add the XmlType attribute to your DocBalanceItem so it knows how the list elements are named.

[XmlType("Document")]
public class DocBalanceItem
{
    [XmlElement("Id")]
    public Guid DocId { get; set; }

    [XmlElement("Balance")]
    public decimal? BalanceAmount { get; set; }
}

Then modify your DeserializeDocBalances() method to return a List<T> and pass the serializer an XmlRootAttribute instance to instruct it to look for Root as the root element:

public List<T> DeserializeList<T>(string filePath)
{
    var itemList = new List<T>();

    if (File.Exists(filePath))
    {
        var serializer = new XmlSerializer(typeof(List<T>), new XmlRootAttribute("Root"));
        TextReader reader = new StreamReader(filePath);
        itemList = (List<T>)serializer.Deserialize(reader);
        reader.Close();
    }

    return itemList;
}

Then you should be able to do

var list = DeserializeList<DocBalanceItem>("somefile.xml");

Since the method now returns a generic List<T>, you no longer need to create custom collections for every type.

P.S. - I tested this solution locally with the provided document, it does work.

like image 73
luksan Avatar answered Oct 21 '22 16:10

luksan