Search code examples
c#xmlxml-parsingxml-deserialization

Deserializing XML from stream to List<T>


I have some data in an XML file which is being read through stream. I've got access to stream object using which I have to deserialize the data and populate a List. My model class is given below

[XmlRoot("invoices")]
public class Invoice
{       
    [XmlElement(ElementName = "description", IsNullable= true)]
    public string Description { get; set; }

    [XmlElement(ElementName = "invoice_date")]
    public string InvoiceDate { get; set; }

    [XmlElement(ElementName = "invoice_number")]
    public string InvoiceNumber { get; set; }

    [XmlElement(ElementName = "date_due")]
    public string DateDue { get; set; }

    [XmlElement(ElementName = "amount")]
    public string Amount { get; set; }
}

And below is the snippet from the method which I am using to read the data

public List<Invoice> ParseRecords(Stream stream)
    {
        List<Invoice> invoices = new List<Invoice>();

        try
        {
            stream.Position = 0;
            XmlSerializer serializer = new XmlSerializer(typeof(List<Invoice>));
            invoices = (List<Invoice>)serializer.Deserialize(stream);
        }
        catch (Exception ex)
        {
            log.Error(ex);
        }

        return invoices;
    }

The XML data contains invoices with a root tag called "invoices" and multiple child records with tag "invoice". There is no namespace defined for the root element and it can't be done as the XML data is received from an external source. The XML data is in correct format. The problem which I face is that when Deserialize() method is executed, it throws an exception saying There is an error in XML document (2, 2). and the inner exception says {" was not expected."}

Can anyone please help me figure out what's wrong here?


Solution

  • One way of achieving the result you are expecting is like below

    [XmlRoot(ElementName = "invoices")]
    public class Invoices
    {
        [XmlElement(ElementName = "invoice")]
        public List<Invoice> Invoice { get; set; }
    }
    
        [XmlRoot("invoice")]
    public class Invoice
    {
        [XmlElement(ElementName = "description", IsNullable = true)]
        public string Description { get; set; }
    
        [XmlElement(ElementName = "invoice_date")]
        public string InvoiceDate { get; set; }
    
        [XmlElement(ElementName = "invoice_number")]
        public string InvoiceNumber { get; set; }
    
        [XmlElement(ElementName = "date_due")]
        public string DateDue { get; set; }
    
        [XmlElement(ElementName = "amount")]
        public string Amount { get; set; }
    }
    

    And your parse records function should like something like below

    public Invoices ParseRecords(Stream stream)
    {
        Invoices invoices;
    
        try
        {
            stream.Position = 0;
            XmlSerializer serializer = new XmlSerializer(typeof(Invoices);
            invoices = (Invoices)serializer.Deserialize(stream);
        }
        catch (Exception ex)
        {
            log.Error(ex);
        }
    
        return invoices;
    }