0

I have the following XML from which I need to map the "DUE" and "RATE" to a list of objects with XmlSerializer. There can be zero to many, and they're always coming as a pair with the same "idx".

<INVOICE ID="4">
    <STATUS>S</STATUS>
    <TOTAL>6230.00</TOTAL>
    <DUE idx="1">14.12.17</DUE>
    <RATE idx="1">6230.00</RATE>
</INVOICE >
<INVOICE ID="5">
    <STATUS>S</STATUS>
    <TOTAL>3270.00</TOTAL>
    <DUE idx="1">30.11.17</DUE>
    <RATE idx="1">1090.00</RATE>
    <DUE idx="2">07.12.17</DUE>
    <RATE idx="2">1090.00</RATE>
    <DUE idx="3">14.12.17</DUE>
    <RATE idx="3">1090.00</RATE>
</INVOICE>

I have the following setup which is working fine without a list of "Rate" and "Due":

[Serializable]
public class UserInvoicesDto
{
    [XmlElement("INVOICE")]
    public List<UserInvoiceDto> Invoices { get; set; }
}

[Serializable, XmlRoot("INVOICE")]
public class UserInvoiceDto
{
    [XmlAttribute("id")]
    public int InvoiceId { get; set; }
    [XmlElement("TOTAL")]
    public string Total { get; set; }
}

And then I have the following class.

[Serializable]
public class InvoicesDueDates
{
    [XmlAttribute("idx")]
    public string Id { get; set; }
    [XmlElement("DUE")]
    public string DueDate { get; set; }
    [XmlElement("RATE")]
    public string Rate { get; set; }
}

Is it somehow possible?

3

1 Answer 1

1

If you only need to deserialize, you can use do so using XmlSerializer to the following types:

[XmlRoot(ElementName = "DUE")]
public class DueDTO
{
    [XmlAttribute(AttributeName = "idx")]
    public string Idx { get; set; }
    [XmlText]
    public string Text { get; set; }
}

[XmlRoot(ElementName = "RATE")]
public class RateDTO
{
    [XmlAttribute(AttributeName = "idx")]
    public string Idx { get; set; }
    [XmlText]
    public decimal Text { get; set; }
}

[XmlRoot(ElementName = "INVOICE")]
public partial class InvoicesDTO
{
    [XmlAttribute(AttributeName = "ID")]
    public string Id { get; set; }
    [XmlElement(ElementName = "STATUS")]
    public string Status { get; set; }
    [XmlElement(ElementName = "TOTAL")]
    public decimal Total { get; set; }

    [XmlElement(ElementName = "DUE")]
    public List<DueDTO> Due { get; set; }
    [XmlElement(ElementName = "RATE")]
    public List<RateDTO> Rate { get; set; }
}

Then, to combine the Rate and Due list into a single InvoicesDueDates collection, you can use LINQ, e.g. as follows:

public partial class InvoicesDTO
{
    public InvoicesDueDates[] InvoicesDueDates
    {
        get
        {
            // To make suure we handle cases where only a Rate or Due item of a specific index is present,
            // perform left outer joins with all indices on both Rate and Due.
            // https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-left-outer-joins
            var query = from i in Due.Select(d => d.Idx).Concat(Rate.Select(r => r.Idx)).Distinct()
                        join due in Due on i equals due.Idx into dueGroup
                        // Throw an exception if we have more than one due item for a given index
                        let due = dueGroup.SingleOrDefault()
                        join rate in Rate on i equals rate.Idx into rateGroup
                        // Throw an exception if we have more than one rate item for a given index
                        let rate = rateGroup.SingleOrDefault()
                        select new InvoicesDueDates { Id = i, DueDate = due == null ? null : due.Text, Rate = rate == null ? (decimal?)null : rate.Text };
            return query.ToArray();
        }
    }
}

public class InvoicesDueDates
{
    public string Id { get; set; }
    public string DueDate { get; set; }
    public decimal? Rate { get; set; }
}

Notes:

  • This solution takes advantage of the fact that, when XmlSerializer is deserializing a List<T> property and encounters list elements interleaved with other elements, it will append each list element encountered to the growing list.

  • If you re-serialize an InvoicesDTO the result will look like:

      <INVOICE ID="5">
        <STATUS>S</STATUS>
        <TOTAL>3270.00</TOTAL>
        <DUE idx="1">30.11.17</DUE>
        <DUE idx="2">07.12.17</DUE>
        <DUE idx="3">14.12.17</DUE>
        <RATE idx="1">1090.00</RATE>
        <RATE idx="2">1090.00</RATE>
        <RATE idx="3">1090.00</RATE>
      </INVOICE>
    

    Notice that all the information has been retained and re-serialized but the <RATE> and <DUE> sequences have been separated out.

  • If you need to re-serialize with interleaved <RATE> and <DUE> elements, you will have to adopt a different strategy, such as the ones from serializing a list of KeyValuePair to XML or Xml Sequence deserialization with RestSharp.

  • I auto-generated the DTO classes using https://xmltocsharp.azurewebsites.net/ then modified them to fit my naming contentions.

Sample working .Net fiddle.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks a bunch. This got me in the right direction.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.