XmlSerializer 如何读取缺少 xmlns 的 XML?
数据阿明
结论
XML 未声明 xmlns 时,a661_df 及其未加前缀的子元素都属于空命名空间。生成的 DfType 则映射到 http://www.company.com/a661 命名空间,因此 XmlSerializer 会报错:<a661_df xmlns=''> was not expected。如果这类文件在语义上仍属于该命名空间,可以在反序列化前,将未限定的元素名补到目标命名空间。
推荐做法:加载后补齐元素命名空间
下面的代码使用 XDocument 读取 XML。如果根元素处于空命名空间,就将文档中所有未限定的元素移入目标命名空间,再交给 XmlSerializer。已有其他命名空间的元素不会改变。
using System.IO;
using System.Linq;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;
private static DfType LoadDefinitionFile(string path)
{
const string targetNamespace = "http://www.company.com/a661";
XDocument document = XDocument.Load(path);
XElement root = document.Root
?? throw new InvalidDataException("XML 文档没有根元素。");
if (root.Name.Namespace == XNamespace.None)
{
XNamespace ns = targetNamespace;
foreach (XElement element in document
.Descendants()
.Where(e => e.Name.Namespace == XNamespace.None))
{
element.Name = ns + element.Name.LocalName;
}
}
var serializer = new XmlSerializer(typeof(DfType));
using XmlReader reader = document.CreateReader();
return (DfType)serializer.Deserialize(reader)!;
}
对于题目中的 XML,处理后,根元素和 a661_layer 都会属于 http://www.company.com/a661 命名空间,与生成类的映射一致。原本已带有该默认命名空间的 XML 不会被改动。
为什么只覆盖根元素通常不够
XmlRootAttribute 用于映射根元素,类型和子元素的命名空间还会受到 XmlTypeAttribute 及成员映射的影响。只将根元素映射到空命名空间,可能解决根元素的匹配问题,但 a661_layer 等成员仍可能出现命名空间不匹配。若使用 XmlAttributeOverrides 创建另一套映射,需要确认根元素及相关成员的命名空间都符合无 xmlns 文档的实际结构。
注意事项
- 只有当无
xmlns的文件确实代表同一套 XML 词汇时,才应将元素移入目标命名空间。如果它表示另一种格式,应为其定义相应的序列化映射或模型。 - 示例会在根元素处于空命名空间时,将所有未限定的元素移入目标命名空间。如果文档中的某些未限定元素本应留在空命名空间,应按元素路径或名称限制转换范围。
- 原代码先用
File.ReadAllText读取文件,再通过Encoding.ASCII转成字节,可能损坏非 ASCII 字符,也可能与 XML 声明中的encoding="UTF-8"不一致。使用XDocument.Load(path)可让 XML 解析器处理文件编码。
备注:内容仅供参考。
