在C#中使用XmlSerializer进行XML序列化时,当存在类的继承关系,直接序列化子类对象会抛出InvalidOperationException异常,提示无法识别子类的类型。这是因为XmlSerializer在序列化时默认只会识别声明时指定的类型,不会自动处理继承体系中的其他类型,此时需要借助XmlInclude属性来告知序列化器需要处理的所有派生类型。

XmlInclude属性的基本用法
XmlInclude是System.Xml.Serialization命名空间下的一个特性,用于标记在序列化当前类型时,需要一并识别的派生类型。它只能应用在基类上,告知XmlSerializer在序列化基类实例时,如果遇到指定的派生类型,可以按照对应的类型规则进行序列化。
假设我们有一个基类Animal,以及两个派生类Dog和Cat,我们希望能够统一序列化Animal类型的集合,包含不同的派生类实例。
定义基类和派生类
首先在基类上添加XmlInclude特性,指定所有需要支持的派生类型:
using System;
using System.Xml.Serialization;
// 在基类上标记需要支持的派生类型
[XmlInclude(typeof(Dog))]
[XmlInclude(typeof(Cat))]
public class Animal
{
public string Name { get; set; }
}
public class Dog : Animal
{
// 狗的特有属性
public string Breed { get; set; }
}
public class Cat : Animal
{
// 猫的特有属性
public int Age { get; set; }
}
执行序列化操作
接下来创建包含不同派生类实例的集合,使用XmlSerializer进行序列化:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
class Program
{
static void Main()
{
// 创建包含不同派生类实例的基类集合
List<Animal> animals = new List<Animal>
{
new Dog { Name = "大黄", Breed = "金毛" },
new Cat { Name = "小白", Age = 2 }
};
// 创建XmlSerializer实例,指定序列化类型为基类List<Animal>
XmlSerializer serializer = new XmlSerializer(typeof(List<Animal>));
// 将序列化结果输出到控制台
using (StringWriter writer = new StringWriter())
{
serializer.Serialize(writer, animals);
string xmlResult = writer.ToString();
Console.WriteLine(xmlResult);
}
}
}
运行上述代码后,生成的XML会包含每个实例的实际类型信息,例如<Dog>和<Cat>节点,同时会保留各自的特有属性,序列化过程不会抛出异常。
XmlInclude的注意事项
- XmlInclude必须应用在基类上,如果应用在派生类上不会生效,无法让序列化器识别该派生类。
- 如果继承层级较多,比如孙子类继承子类,子类继承基类,那么需要在基类上同时标记子类和孙子类,或者在子类上标记孙子类,但最终序列化器能识别的所有类型都需要通过XmlInclude显式声明。
- 如果派生类型较多,逐个添加XmlInclude会比较繁琐,也可以通过XmlAttributeOverrides动态指定需要序列化的类型,但XmlInclude是更简洁的静态声明方式,适合类型固定的场景。
反序列化时的处理
添加了XmlInclude之后,反序列化时XmlSerializer会根据XML中的节点名称自动识别对应的类型,不需要额外的配置。例如对上述生成的XML进行反序列化:
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Serialization;
class Program
{
static void Main()
{
string xml = @"<?xml version=""1.0"" encoding=""utf-16""?>
<ArrayOfAnimal xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"">
<Dog>
<Name>大黄</Name>
<Breed>金毛</Breed>
</Dog>
<Cat>
<Name>小白</Name>
<Age>2</Age>
</Cat>
</ArrayOfAnimal>";
XmlSerializer serializer = new XmlSerializer(typeof(List<Animal>));
using (StringReader reader = new StringReader(xml))
{
List<Animal> animals = (List<Animal>)serializer.Deserialize(reader);
foreach (var animal in animals)
{
Console.WriteLine($"类型:{animal.GetType().Name},名称:{animal.Name}");
if (animal is Dog dog)
{
Console.WriteLine($"品种:{dog.Breed}");
}
else if (animal is Cat cat)
{
Console.WriteLine($"年龄:{cat.Age}");
}
}
}
}
}
反序列化后可以得到正确的派生类实例,并且各自的特有属性也会被正确赋值,不会出现类型丢失的问题。
XmlSerializerC#_XmlIncludeXML序列化继承序列化修改时间:2026-07-11 23:18:12