1

I'm writing a Web API 2 controller in MVC 5. At present, my XML output looks like this:

<ArrayOfVoucher xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MyAPI.Models">
    <Voucher>
        <VoucherDate>2018-04-04</VoucherDate>
        <VoucherNumber>123</VoucherNumber>
        <VoucherTransactions>
            <VoucherTransaction>
                <TransDate>2018-04-03</TransactionDate>
                <TransType>GL</TransactionType>
            </VoucherTransaction>
            <VoucherTransaction>
                <TransDate>2018-04-03</TransactionDate>
                <TransType>GL</TransactionType>
            </VoucherTransaction>
        </VoucherTransactions>
    <Voucher>
</ArrayOfVoucher>

However, the specification I'm working to states that it should look like this:

<ArrayOfVoucher xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/MyAPI.Models">
    <Voucher>
        <VoucherDate>2018-04-04</VoucherDate>
        <VoucherNumber>123</VoucherNumber>
        <Transaction>
            <TransDate>2018-04-03</TransactionDate>
            <TransType>GL</TransactionType>
        </Transaction>
        <Transaction>
            <TransDate>2018-04-03</TransactionDate>
            <TransType>GL</TransactionType>
        </Transaction>
    <Voucher>
</ArrayOfVoucher>

Note the differences. In the second sample:

  • The VoucherTransaction nodes are not grouped in together in a node called 'VoucherTransactions'.
  • The VouncherTransaction nodes are named Transaction.

The second one I can achieve by modifying the definition of my data model (see below), although I wonder if there's a better way, since the names of the model's properties at present are sensible.

My main question relates to that first point, though. How do I prevent the VoucherTransaction nodes being grouped in a VoucherTransactions node?

These are my data model classes:

Voucher

public class Voucher
{
    public string VoucherNumber { get; set; }
    public DateTime VoucherDate { get; set; }
    public List<VoucherTransaction> VoucherTransactions { get; set; }
}

VoucherTransaction

public class VoucherTransaction
{
    public string TransType { get; set; }
    public DateTime TransDate { get; set; }
}

And the code currently creating the list is this:

foreach (SalesLedgerTransaction t in salesLedgerTransactions)
{
    Voucher voucher = new Voucher
    {
        VoucherNo = "1",
        VoucherDate = t.TransactionDate,
        VoucherTransactions = new List<VoucherTransaction>()
    };

    VoucherTransaction arTransaction = new VoucherTransaction
    {
        TransType = "AR",
        TransDate = t.TransactionDate
    };
    voucher.VoucherTransactions.Add(arTransaction);

    VoucherTransaction glTransaction = new VoucherTransaction
    {
        TransType = "GL",
        TransDate = t.TransactionDate
    };
    voucher.VoucherTransactions.Add(glTransaction);

    vouchers.Add(voucher);
}

So yeah, if anyone can help me manipulate the XML so that the Voucher Transactions aren't grouped in a sub-node that'd be cool. Extra appreciation for help on specifying node names which differ from the models' properties (and the name of the root node - ArrayOfVoucher isn't per the spec. either!).

Philip Stratford
  • 4,513
  • 4
  • 45
  • 71
  • Try annotating the `VoucherTransactions` property with the `XmlArrayItem` attribute. – Pawel Apr 04 '18 at 17:58
  • To eliminate the outer `` element and rename the `` element when serializing your `List VoucherTransactions`, apply `[XmlElement("Transaction")]` as shown in [Deserializing into a List without a container element in XML](https://stackoverflow.com/q/5271442/3744182). – dbc Apr 04 '18 at 17:58
  • Also, if you are using `DataContractSerializer`, you may need to switch to `XmlSerializer`. See [Data Contract Serializer - How to omit the outer element of a collection](https://stackoverflow.com/q/8591045/3744182). – dbc Apr 04 '18 at 19:10
  • @dbc `[XmlElement("Transaction")`, having read up about it, seems like it should do exactly what I need, but it doesn't work. I've decorated the `VoucherTransactions` property of the `Voucher` class with it but the XML output doesn't change. In fact, nothing I do with XML decorations on my properties makes any difference. I'm wondering if this is because my Controller isn't explicitly performing any XML serialisation, I'm just returning a `List` and allowing the client to specify whether it wants XML or JSON, which I feel is best practice and requires less code! – Philip Stratford Apr 05 '18 at 10:30
  • 1
    @PhilipStratford - that's why I think you may also need to switch to using `XmlSerializer`. You can do it e.g. by setting `GlobalConfiguration.Configuration.Formatters.XmlFormatter.UseXmlSerializer = true` as shown [here](https://learn.microsoft.com/en-us/aspnet/web-api/overview/formats-and-model-binding/json-and-xml-serialization#xml-media-type-formatter). – dbc Apr 05 '18 at 11:28
  • Ah I didn't realise I was using `DataContractSerializer`, that link really helped, thanks. It's all working now, thanks for your help. Not sure how I go about marking this question as answered now! – Philip Stratford Apr 05 '18 at 13:00
  • @PhilipStratford - I think it's a duplicate of [Deserializing into a List without a container element in XML](https://stackoverflow.com/q/5271442/3744182) plus [Data Contract Serializer - How to omit the outer element of a collection](https://stackoverflow.com/q/8591045/3744182) plus [WEB API to return object following serialization attributes](https://stackoverflow.com/q/23843320/3744182) and/or [WebAPI Serialization problems when consuming Recurly Rest API which returns XML](https://stackoverflow.com/a/18390969/3744182). I could close it off as such if you want. – dbc Apr 05 '18 at 18:19

0 Answers0