【问题标题】:How can I control the name of an XML element when using custom serialization in C#?在 C# 中使用自定义序列化时如何控制 XML 元素的名称?
【发布时间】:2020-08-31 18:12:04
【问题描述】:

我正在使用需要遵守的外部开发 XSD,其中字段 Engine 可以是以下两种类型之一:ElectricCombustion。两者都派生自超类Engine

有多种方法可以让 C# 中的 XmlSerializer 处理此问题,但似乎都与相同的副作用相关:使用自定义序列化程序时,默认的序列化命名约定被抛弃了。我想模仿 Vehicle 类的其余字段的默认行为。

考虑以下几点:

using System;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

namespace DMV
{
    public class Vehicle : IXmlSerializable
    {
        public Engine Engine { get; set; }
        [XmlElement("Manufacturer")]
        public Company Manufacturer { get; set; }

        public void WriteXml(XmlWriter writer)
        {
            XmlSerializer engineSerializer = new XmlSerializer(Engine.GetType());
            engineSerializer.Serialize(writer, Engine);
            
            XmlSerializer manufacturerSerializer = new XmlSerializer(Manufacturer.GetType());
            manufacturerSerializer.Serialize(writer, Manufacturer);
        }
    }

    public abstract class Engine { }

    public class Electric : Engine
    {
        public int Poles { get; set; }
    }

    public class Combustion : Engine
    {
        public int CC { get; set; }
    }

    public class Company 
    {
        public string Name { get; set; }
    }

}

我尝试使用以下序列化代码运行它:

Vehicle vehicle = new Vehicle();
vehicle.Engine = new Electric();
vehicle.Manufacturer = new Company();

XmlSerializer serializer = new XmlSerializer(typeof(Vehicle));
TextWriter writer = new StringWriter();
serializer.Serialize(writer, vehicle);

Console.WriteLine(writer.ToString());
writer.Close();

结果是下面一堆令人失望的 caballus 粪便:

<Vehicle>
  <Electric>
    <Poles>0</Poles>
  </Electric>
  <Company/>
</Vehicle>

(是的,Engine 字段已正确序列化,但Company 没有那么多。)

如果我使用默认(内置)序列化,我会得到以下(期望的!)输出:

<Vehicle>
  <Manufacturer />
</Vehicle>

如何既序列化派生类ElectricCombustion,同时保留字段ManufacturerCompany 类型)的默认命名约定?

【问题讨论】:

    标签: c# xml .net-core serialization


    【解决方案1】:

    您可以通过这样做摆脱自定义序列化程序:

    public class Vehicle
    {
        [XmlElement(typeof(Electric), ElementName = "Electric")]
        [XmlElement(typeof(Combustion), ElementName = "Combustion")]
        public Engine Engine { get; set; }
        [XmlElement("Manufacturer")]
        public Company Manufacturer { get; set; }
    }
    

    【讨论】:

      【解决方案2】:

      尝试将specific constructor 用于XmlSerializer,它接受“其他类型”的数组来处理您的派生类,而不是实现IXmlSerializable 并手动进行序列化。序列化程序现在将处理您的派生类,但也会以您想要的方式获取您的其他属性(例如XmlElement):

      using System;
      using System.IO;
      using System.Xml.Serialization;
      
      namespace SomeNamespace
      {
          public class Program
          {
              static void Main()
              {
                  Vehicle vehicle = new Vehicle();
                  vehicle.Engine = new Electric();
                  vehicle.Manufacturer = new Company();
      
                  XmlSerializer serializer = new XmlSerializer(typeof(Vehicle), new Type[] { typeof(Combustion), typeof(Electric)});
                  TextWriter writer = new StringWriter();
                  serializer.Serialize(writer, vehicle);
      
                  Console.WriteLine(writer.ToString());
                  writer.Close();
              }
          }
      
          public class Vehicle
          {
              public Engine Engine { get; set; }
              [XmlElement("Manufacturer")]
              public Company Manufacturer { get; set; }
          }
      
          public abstract class Engine { }
      
          public class Electric : Engine
          {
              public int Poles { get; set; }
          }
      
          public class Combustion : Engine
          {
              public int CC { get; set; }
          }
      
          public class Company
          {
              public string Name { get; set; }
          }
      
      }
      

      输出:

      <?xml version="1.0" encoding="utf-16"?>
      <Vehicle xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
        <Engine xsi:type="Electric">
          <Poles>0</Poles>
        </Engine>
        <Manufacturer />
      </Vehicle>
      

      与公认的答案不同,这种方法的优点是不会将基类链接到所有可能的派生类。 (任何时候您在该方法中添加一个新的派生类型,您都必须返回并编辑基本类型。)虽然这个例子有Program.Main() 硬编码派生类型的数组,但您可以很容易地使用反射来构建该数组,使得添加新的派生类型变得更加容易。

      【讨论】:

        【解决方案3】:

        有两种方法可以做到这一点。

        1. XmlRoot 属性添加到Company 类定义中:

           [XmlRoot("Manufacturer")]
           public class Company
          
        2. XmlRootAttribute 添加到XmlSerializer 构造函数。

           manufacturerSerializer = new XmlSerializer(Manufacturer.GetType(),
               new XmlRootAttribute("Manufacturer"));
          

          注意这个:Dynamically Generated Assemblies

        【讨论】:

        • 感谢亚历山大的意见。我应该在我的描述中添加 Company 正在 XML 中的其他上下文中使用,例如 RentalCompany,因此不能在类级别绑定到特定名称。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-12
        • 1970-01-01
        相关资源
        最近更新 更多