C# XML 序列化删除包装元素

Pet*_*ter 2 c# xml xml-serialization

我写这篇文章是因为我对 XmlSerializer 有问题。我想要以下格式的 XML:

<?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>
Run Code Online (Sandbox Code Playgroud)

所以我创建了以下课程。

[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; }
}
Run Code Online (Sandbox Code Playgroud)

我在删除包装元素(删除 RootBody)时遇到问题。我读过类似的主题,例如此链接。但这并不能解决我的问题。

Man*_*mer 5

在实际解释之前,让我指出一些非常重要的事情:

  • 这个 XML 设计得不是很好,并且会导致很多其他问题(我猜它实际上比这个复杂得多)。
  • 命名约定不一致 (e-InvoiceTradeInvoce)
  • 版本号看起来不合适,在投入额外时间之前确保这确实是他们(无论是谁告诉你这样做的)想要的。
  • 此 XML 未定义名称空间 并且可能也没有XSDDTD
  • 查看Google 的 XML 设计指南。你会发现还有很大的改进空间。

有很多不同的方法可以做到这一点,这只是其中之一。我建议您与负责此设计的任何人联系,并尝试改变他们的想法。

由于您想要序列化e-Invoice并且TradeInvoce没有包装器元素,但仍保持元素的顺序(因为它们属于在一起),因此您需要确保它们具有公共基类,以便可以从同一集合序列化它们。

这个抽象Invoice类只是通过属性告诉序列化器在序列化期间应包含哪些类XmlInclude

[XmlInclude(typeof(EInvoice))]
[XmlInclude(typeof(TradeInvoice))]
public abstract class Invoice
{
}
Run Code Online (Sandbox Code Playgroud)

您的实际课程将基本保持不变

[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; }
}
Run Code Online (Sandbox Code Playgroud)

您的数据类不再需要任何与 XML 相关的属性,因为现在它无法直接序列化为该格式。

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

    public EInvoice EInvoiceInfo { get; set; }

    public TradeInvoice TradeInvoice { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

你的Root课程必须简化一点。由于我们希望EInvoceTradeInvoiceElements 处于同一级别但混合在一起,因此我们需要将它们放在同一个集合中。属性XmlElement将告诉序列化器如何在不依赖xsi:type属性的情况下处理不同类型的元素。

[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; }
}
Run Code Online (Sandbox Code Playgroud)

此时序列化非常简单。只需将所有元素依次添加到元素集合中即可:

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);
}
Run Code Online (Sandbox Code Playgroud)

反序列化也不是很难,但很容易出错并做出很多假设:

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]
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

这是一个工作演示 Fiddle,它输出 XML