如何实现XmlSerializer序列化用新类名、反序列化兼容新旧类名?
场景与问题
我原本有如下类定义:
public class Foo { }
它可反序列化为以下XML,这类文件已存在于客户端机器中:
<?xml version="1.0"?> <Foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> </Foo>
现在要将该类重命名为Footastic:
public class Footastic { }
新序列化操作需生成根节点为Footastic的XML:
<?xml version="1.0"?> <Footastic xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> </Footastic>
核心需求:反序列化时同时兼容旧根节点Foo和新根节点Footastic,但新序列化只能输出Footastic作为根节点。直接给Footastic添加[XmlRoot("Foo")]特性会导致新序列化也输出Foo,不符合要求。
现有序列化/反序列化代码如下:
现有反序列化代码
public static T? Deserialize<T>(string fileName) { if (!File.Exists(fileName)) return default; using FileStream streamReader = new(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using var xr = XmlReader.Create(streamReader); XmlSerializer xmlDeSerializer = new(typeof(T)); return (T?)xmlDeSerializer.Deserialize(xr); }
现有序列化代码
public static void Serialize<T>(T? value, string fileName) { if (value is null) return; Directory.CreateDirectory(Path.GetDirectoryName(fileName) ?? ""); using FileStream fileStream = new(fileName, FileMode.Create, FileAccess.Write, FileShare.Write); using var streamWriter = XmlWriter.Create(fileStream, new() { Encoding = Encoding.UTF8, Indent = true }); XmlSerializer xmlSerializer = new(typeof(T)); xmlSerializer.Serialize(streamWriter, value); }
解决方案
方法一:修改XML根节点后反序列化
读取XML内容,将旧根节点Foo精准替换为Footastic后再进行反序列化,适合结构简单的XML场景:
public static T? Deserialize<T>(string fileName) { if (!File.Exists(fileName)) return default; // 使用XDocument精准修改根节点名称 var doc = XDocument.Load(fileName); if (doc.Root != null && doc.Root.Name.LocalName == "Foo") { doc.Root.Name = "Footastic"; } using var stringReader = new StringReader(doc.ToString()); using var xr = XmlReader.Create(stringReader); XmlSerializer xmlDeSerializer = new(typeof(T)); return (T?)xmlDeSerializer.Deserialize(xr); }
方法二:动态创建兼容的XmlSerializer实例
先检测XML的根节点名称,根据名称创建对应配置的XmlSerializer:如果根节点是Foo,就创建指定根节点为Foo的序列化器;否则使用默认序列化器。这种方法无需修改XML内容,适合复杂XML场景:
public static T? Deserialize<T>(string fileName) { if (!File.Exists(fileName)) return default; XmlSerializer serializer; // 先读取根节点名称 using (var xr = XmlReader.Create(fileName)) { xr.MoveToContent(); var rootName = xr.LocalName; serializer = rootName == "Foo" ? new XmlSerializer(typeof(T), new XmlRootAttribute("Foo")) : new XmlSerializer(typeof(T)); } using FileStream streamReader = new(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); using var xr = XmlReader.Create(streamReader); return (T?)serializer.Deserialize(xr); }
序列化代码无需改动
原序列化代码保持不变即可,默认情况下XmlSerializer会使用类名Footastic作为根节点,生成符合要求的XML。
内容的提问来源于stack exchange,提问作者Rand Random
相关产品推荐
相关产品推荐

