反序列化具有未知元素顺序的 XML

Bob*_*son 2 c# serialization soap xml-serialization xmlserializer

我正在尝试为一项规格确实不足的服务实现一个客户端。它类似于 SOAP,尽管它没有 WSDL 或等效文件。该规范也没有提供有关元素正确排序的任何信息 - 它们在规范中按字母顺序列出,但如果它们在请求中的顺序不正确,则服务会返回 XML 解析错误(所述顺序由检查示例)。

我可以它来提交请求,即使这很痛苦。但是,我不知道如何正确处理响应。

使用两者SoapEnvelope和直接使用XmlSerializer,如果响应包含我尚未正确订购的元素,它会显示null在我的对象上。再次,我可以设法处理这个问题,并使用 attribute 手动排序类属性Order我无法判断原始 XML 是否有一个我没有正确排序并因此保留为 的字段null

这引出了当前的问题: 如何检查 XmlSerializer 是否删除了字段

dbc*_*dbc 7

您可以使用XmlSerializer.UnknownElementon 事件XmlSerializer来捕获无序元素。这将允许您手动查找并修复反序列化中的问题。

\n

更复杂的答案是在序列化时正确排序元素,但在反序列化时忽略顺序。这需要使用XmlAttributes类和XmlSerializer(Type,\xe2\x80\x82XmlAttributeOverrides)构造函数。请注意,以这种方式构建的序列化器必须缓存在哈希表中并重新使用,以避免严重的内存泄漏,因此这个解决方案有点“挑剔”,因为 Microsoft 没有提供有意义的GetHashCode()for XmlAttributeOverrides. 以下是一种可能的实现,它取决于提前了解需要忽略其属性XmlElementAttribute.OrderXmlArrayAttribute.Order属性的所有类型,从而避免需要创建复杂的自定义哈希方法:

\n
 // /sf/ask/2345469591/\npublic class XmlSerializerFactory : XmlOrderFreeSerializerFactory\n{\n    static readonly XmlSerializerFactory instance;\n\n    // Use a static constructor for lazy initialization.\n    private XmlSerializerFactory()\n        : base(new[] { typeof(Type2), typeof(Type1), typeof(TestClass), typeof(Type3) }) // These are the types in your client for which Order needs to be ignored whend deserializing\n    {\n    }\n\n    static XmlSerializerFactory()\n    {\n        instance = new XmlSerializerFactory();\n    }\n\n    public static XmlSerializerFactory Instance { get { return instance; } }\n}\n\npublic abstract class XmlOrderFreeSerializerFactory\n{\n    readonly XmlAttributeOverrides overrides;\n    readonly object locker = new object();\n    readonly Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>();\n\n    static void AddOverrideAttributes(Type type, XmlAttributeOverrides overrides)\n    {\n        if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string))\n            return;\n\n        var mask = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public;\n        foreach (var member in type.GetProperties(mask).Cast<MemberInfo>().Union(type.GetFields(mask)))\n        {\n            XmlAttributes overrideAttr = null;\n            foreach (var attr in member.GetCustomAttributes<XmlElementAttribute>())\n            {\n                overrideAttr = overrideAttr ?? new XmlAttributes();\n                overrideAttr.XmlElements.Add(new XmlElementAttribute { DataType = attr.DataType, ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace, Type = attr.Type });\n            }\n            foreach (var attr in member.GetCustomAttributes<XmlArrayAttribute>())\n            {\n                overrideAttr = overrideAttr ?? new XmlAttributes();\n                overrideAttr.XmlArray = new XmlArrayAttribute { ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace };\n            }\n            foreach (var attr in member.GetCustomAttributes<XmlArrayItemAttribute>())\n            {\n                overrideAttr = overrideAttr ?? new XmlAttributes();\n                overrideAttr.XmlArrayItems.Add(attr);\n            }\n            foreach (var attr in member.GetCustomAttributes<XmlAnyElementAttribute>())\n            {\n                overrideAttr = overrideAttr ?? new XmlAttributes();\n                overrideAttr.XmlAnyElements.Add(new XmlAnyElementAttribute { Name = attr.Name, Namespace = attr.Namespace });\n            }\n            if (overrideAttr != null)\n                overrides.Add(type, member.Name, overrideAttr);\n        }\n    }\n\n    protected XmlOrderFreeSerializerFactory(IEnumerable<Type> types)\n    {\n        overrides = new XmlAttributeOverrides();\n        foreach (var type in types.SelectMany(t => t.BaseTypesAndSelf()).Distinct())\n        {\n            AddOverrideAttributes(type, overrides);\n        }\n    }\n\n    public XmlSerializer GetSerializer(Type type)\n    {\n        if (type == null)\n            throw new ArgumentNullException("type");\n        lock (locker)\n        {\n            XmlSerializer serializer;\n            if (!serializers.TryGetValue(type, out serializer))\n                serializers[type] = serializer = new XmlSerializer(type, overrides);\n            return serializer;\n        }\n    }\n}\n\npublic static class TypeExtensions\n{\n    public static IEnumerable<Type> BaseTypesAndSelf(this Type type)\n    {\n        while (type != null)\n        {\n            yield return type;\n            type = type.BaseType;\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

然后在反序列化类型时,使用XmlSerializer工厂提供的类型。鉴于它SoapEnvelope是 的子类XmlDocument,您应该能够按照Deserialize object property with StringReader vs XmlNodeReader中的答案反序列化主体节点。

\n

注意——仅经过适度测试。演示小提琴在这里

\n