Native AOT编译下XmlSerializer反序列化报错排查及疑问:为何默认XmlSerializer反序列化需要动态代码生成?
Native AOT编译下XmlSerializer反序列化报错排查及疑问:为何默认XmlSerializer反序列化需要动态代码生成?
最近我在给公司做一个解析WCF服务MEX端点数据的工具,功能本身跑起来都顺顺当当的,直到产品那边提了个常规需求:把.NET项目编译成Native AOT——理由无非是不用装.NET运行时,还能提升性能。结果刚到测试环节就炸了,报了个让人一头雾水的错误:
System.InvalidOperationException HResult=0x80131509 Message = There is an error in XML document (0, 0). Source = System.Private.Xml Stack trace: in System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader, String encodingStyle, XmlDeserializationEvents events) in System.Xml.Serialization.XmlSerializer.Deserialize(XmlReader xmlReader) in System.Web.Services.Description.ServiceDescription.Read(XmlReader reader, Boolean validate) in System.Web.Services.Description.ServiceDescription.Read(Stream stream, Boolean validate) in DocSynth.Remote.WCF.Services.WSDLParser.ReadWSDLFiles(IEnumerable`1 filePaths) in DocSynth.Remote.WCF.Services.WCFMEXMetadataProvider.ParseWCFFiles(WCFFilesPreparationResult files) Initial Stack trace: System.ArgumentNullException.Throw(string) System.ArgumentNullException.ThrowIfNull(object, string) System.Xml.Serialization.XmlReflectionImporter.ImportTypeMapping(System.Type, System.Xml.Serialization.XmlRootAttribute, string) System.Xml.Serialization.XmlSerializer.GetMapping() System.Xml.Serialization.XmlSerializer.Deserialize(System.Xml.XmlReader, string, System.Xml.Serialization.XmlDeserializationEvents) Internal Exception 1: ArgumentNullException: Value cannot be null. Arg_ParamName_Name
什么情况?XML文档(0,0)出错?我百分百确定WSDL文件没问题啊——这是从MEX端点直接拿的,我手动检查过,完全没有XML语法问题。
我开始一步步死磕这个问题:
- 一开始怀疑是流的位置问题,比如XMLTextReader读取流时会自动偏移4096字节,试着把流的Position设回0,没用。
- 接着逐行调试代码,发现调用
ServiceDescription.Read(fs)时,走的是XmlSerializer的反射反序列化分支,而不是自定义的反序列化逻辑。 - 追进源码看,发现
GetMapping方法里的_rootType是空的,直接触发了空指针异常。 - 再看ServiceDescription里的自定义Serializer:它是XmlSerializer的子类,重写了CreateReader、CreateWriter这些核心方法,本身根本不需要
_rootType——因为自定义读写器会处理所有解析逻辑。那为什么会走到反射分支? - 查
ShouldUseReflectionBasedSerialization方法的实现,发现当_mapping为空(这个自定义Serializer确实没初始化mapping),就只看Mode是不是ReflectionOnly。 - 最后看到
Mode的核心逻辑:
瞬间恍然大悟!我两周前开了项目属性里的「Enable native AOT publication」开关,Native AOT环境下internal static SerializationMode Mode { get => RuntimeFeature.IsDynamicCodeSupported ? s_mode : SerializationMode.ReflectionOnly; set => s_mode = value; }RuntimeFeature.IsDynamicCodeSupported必然是false,所以Mode直接被设为了ReflectionOnly。而这个自定义Serializer完全没做反射反序列化的适配,直接就空指针炸了。
最后只能给产品反馈:要是想用Native AOT编译,得自己写一套WSDL解析逻辑,现成的ServiceDescription用不了了。
不过我还是满肚子疑惑:为什么XmlSerializer在Native AOT编译的程序里,反序列化需要依赖动态代码生成?按说Native AOT是把所有类型都提前编译打包进可执行文件了,反射根本不应该用来动态创建或修改类型啊?
备注:内容来源于stack exchange,提问作者ZecosMAX
相关产品推荐
相关产品推荐

