1

I wrote because I have problem with XmlSerializer. I want XML in the following format:

<?xml version="1.0" encoding="utf-8"?>
<RootXML>
    <e-Invoice>
      <Version>1.03</Version>
    </e-Invoice>
    <TradeInvoice>
        <Id>1</Id>
        <Value>100</Value>
    </TradeInvoice>
    <e-Invoice>
      <Version>1.03</Version>
    </e-Invoice>
    <TradeInvoice>
        <Id>2</Id>
        <Value>200</Value>
    </TradeInvoice>
    <e-Invoice>
        <Version>1.03</Version>
    </e-Invoice>
    <TradeInvoice>
        <Id>3</Id>
        <Value>300</Value>
    </TradeInvoice>
</RootXML>

So I created the following classes.

[XmlRoot("RootXML")]
public class Root
{
    public Root()
    {
        RootBodies = new List<RootBody>();
    }

    [XmlElement("e-Invoice")]
    public List<RootBody> RootBodies { get; set; }
}

public class RootBody
{
    public RootBody()
    {
        TradeInvoice = new TradeInvoice();
        EInvoiceInfo = new Version(); ;
    }

    [XmlElement("e-Invoice")]
    public Version EInvoiceInfo { get; set; }

    [XmlElement("TradeInvoice")]
    public TradeInvoice TradeInvoice { get; set; }
}

public class Version
{
    [XmlElement("Version")]
    public string Version { get; set; }
}

public class TradeInvoice
{
    [XmlElement("Id")]
    public int Id { get; set; }
    [XmlElement("Value")]
    public int Value { get; set; }
}

I have problem with removing wraper Elements (remove RootBody). I read similar topic like this link. But it does not solve my problem.

Community
  • 1
  • 1
Peter
  • 23
  • 1
  • 4
  • 2
    The XML you are aiming for is not very well designed. If `e-Invoice` and `TradeInvoice` belong together (which they apparently do), they should be serialized together (wrapped). – Manfred Radlwimmer Feb 15 '17 at 09:20
  • I agree Manfred, relying on a property being located directly after another and thus concluding they belong together isn´t a good idea. Instead mark also in the xml that they belong to a single instance of your type. Or do you have an external schema that forces you to format the xml this way? – MakePeaceGreatAgain Feb 15 '17 at 09:23
  • 1
    I don't created this XML, this xml created outer company, my client. – Peter Feb 15 '17 at 09:26
  • 1
    @Peter I'll put together an example how this can be achieved - it won't be pretty but it should work (one-way). – Manfred Radlwimmer Feb 15 '17 at 09:28

1 Answers1

4

Before the actual explanation, let me point out a couple of very important things:

  • This XML is not very well designed and will cause a lot of other problems along the line (I'm guessing it is actually a lot more complicated than this).
  • The naming convention is inconsistent (e-Invoice and TradeInvoce)
  • The version number looks out of place, make sure this is really what they (whoever told you to do this) want before investing additional time.
  • This XML defines no namespaces (and probably doesn't have an XSD or DTD either)
  • Take a look at Google's XML design guidelines. You'll realize there is a lot of room for improvement.

There are a lot of different ways to do this, this is just one of them. I recommend you instead take it up with whoever is responsible for this design and try to change their mind.

Since you want to serialize e-Invoice and TradeInvoce without a wrapper element but still keep the order of elements (because they belong together) you need to make sure they have a common base class so they can be serialized from the same collection.

This abstract Invoice class simply tells the serializer which classes should be included during serialization via the XmlInclude attribute.

[XmlInclude(typeof(EInvoice))]
[XmlInclude(typeof(TradeInvoice))]
public abstract class Invoice
{
}

Your actual classes will be mostly unchanged

[XmlRoot("e-Invoice")]
public class EInvoice : Invoice
{
    [XmlElement("Version")]
    public string Version { get; set; }
}

[XmlRoot("TradeInvoice")]
public class TradeInvoice : Invoice
{
    [XmlElement("Id")]
    public int Id { get; set; }
    [XmlElement("Value")]
    public int Value { get; set; }
}

Your data class doesn't need any XML-related Attributes anymore because as it is now, it can't be serialized into that format directly.

public class InvoicePair
{
    public InvoicePair(EInvoice eInvoice, TradeInvoice tradeInvoice)
    {
        TradeInvoice = tradeInvoice;
        EInvoiceInfo = eInvoice;
    }

    public EInvoice EInvoiceInfo { get; set; }

    public TradeInvoice TradeInvoice { get; set; }
}

Your Root class has to be simplified a bit. Since we want EInvoce and TradeInvoice Elements on the same level but mixed together, we need to put them in the same collection. The XmlElement attributes will tell the serializer how to handle elements of different types without relying on the xsi:type attribute.

[XmlRoot("RootXML")]
public class Root
{
    public Root()
    {
        RootBodies = new List<Invoice>();
    }

    [XmlElement(Type = typeof(EInvoice), ElementName = "e-Invoice")]
    [XmlElement(Type = typeof(TradeInvoice), ElementName = "TradeInvoice")]
    public List<Invoice> RootBodies { get; set; }
}

Serialization is quite straight-forward at this point. Simply add all elements to the collection of elements one after another:

public static void Serialize(IEnumerable<InvoicePair> invoices, Stream stream)
{
    Root root = new Root();
    foreach (var invoice in invoices)
    {
        root.RootBodies.Add(invoice.EInvoiceInfo);
        root.RootBodies.Add(invoice.TradeInvoice);
    }

    XmlSerializer serializer = new XmlSerializer(typeof(Root));
    serializer.Serialize(stream, root);
}

Deserialization isn't very hard either, but very prone to erros and makes a lot of assumptions:

public static IEnumerable<InvoicePair> Deserialize(Stream stream)
{
    XmlSerializer serializer = new XmlSerializer(typeof(Root));
    Root root = serializer.Deserialize(stream) as Root;

    for (int i = 0; i < root.RootBodies.Count; i += 2)
    {
        yield return new InvoicePair(
            (EInvoice) root.RootBodies[i], 
            (TradeInvoice) root.RootBodies[i+1]
        );
    }
}

Here is a working Demo Fiddle, that outputs the XML

Manfred Radlwimmer
  • 13,257
  • 13
  • 53
  • 62
  • I'm really grateful for your help.I joined your code with my modifications to my project. Your solution works. – Peter Feb 15 '17 at 17:59