反序列化Excel XML文件时属性无法正常反序列化的解决方法
问题场景
有一份从Excel导出的XML文件,核心结构如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?mso-application progid="Excel.Sheet"?> <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40"> ... <Worksheet ss:Name="Page1"> ... </Worksheet> </Workbook>
对应的C#序列化模型定义如下:
using System.Xml.Serialization; using System.Collections.Generic; [XmlRoot(ElementName = "Worksheet", Namespace = "urn:schemas-microsoft-com:office:spreadsheet")] public class Worksheet { [XmlAttribute(AttributeName = "Name", Namespace = "urn:schemas-microsoft-com:office:spreadsheet")] public string Name { get; set; } } [XmlRoot(ElementName = "Workbook", Namespace = "urn:schemas-microsoft-com:office:spreadsheet")] public class Workbook { [XmlElement(ElementName = "Worksheet", Namespace = "urn:schemas-microsoft-com:office:spreadsheet")] public List<Worksheet> Worksheet { get; set; } }
尝试两种反序列化方式(使用XmlNamespaceManager或直接读取文件),均出现Worksheet.Name属性为null的情况。但修改XML和模型中ss命名空间的声明值后,属性可正常读取。需要在保留原命名空间的前提下解决该问题。
原因分析
问题源于XML中命名空间的冲突:根节点Workbook同时声明了默认命名空间xmlns="urn:schemas-microsoft-com:office:spreadsheet"和别名空间xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet",二者指向同一个命名空间。XmlSerializer处理时,带前缀的属性(如ss:Name)会被视为独立的命名空间限定属性,即使前缀对应的命名空间与默认一致,也需要明确匹配前缀的映射规则,否则无法正确绑定到模型属性。
解决方法
方案1:修改模型属性的XmlAttribute特性
直接调整Worksheet类中Name属性的特性,明确指定属性的前缀为ss,并设置Form为XmlSchemaForm.Qualified:
[XmlRoot(ElementName = "Worksheet", Namespace = "urn:schemas-microsoft-com:office:spreadsheet")] public class Worksheet { [XmlAttribute(AttributeName = "Name", Namespace = "urn:schemas-microsoft-com:office:spreadsheet", Form = XmlSchemaForm.Qualified, Prefix = "ss")] public string Name { get; set; } }
修改后,使用原有的反序列化代码即可正常读取Name属性值。
方案2:使用XmlAttributeOverrides动态调整序列化规则
如果无法修改原模型类,可以在创建XmlSerializer时通过XmlAttributeOverrides动态覆盖属性的序列化配置:
// 创建属性覆盖配置 var overrides = new XmlAttributeOverrides(); var attrs = new XmlAttributes(); var nameAttr = new XmlAttributeAttribute { AttributeName = "Name", Namespace = "urn:schemas-microsoft-com:office:spreadsheet", Form = XmlSchemaForm.Qualified, Prefix = "ss" }; attrs.XmlAttribute = nameAttr; overrides.Add(typeof(Worksheet), "Name", attrs); // 使用覆盖配置创建序列化器 var serializer = new XmlSerializer(typeof(Workbook), overrides); // 执行反序列化 using var fs = new FileStream("so.xml", FileMode.Open); var wb = serializer.Deserialize(fs) as Workbook; var ws = wb.Worksheet.First(); Console.WriteLine(ws.Name); // 输出:Page1
原理说明
当XML属性使用前缀标记时,XmlSerializer会严格匹配属性的前缀+命名空间组合。通过设置Form = XmlSchemaForm.Qualified,强制属性使用命名空间限定,并指定Prefix = "ss",即可让序列化器正确识别XML中的ss:Name属性,完成与模型属性的绑定。
内容的提问来源于stack exchange,提问作者S. Inf

