2015-11-03 90 views
1

我正在嘗試爲具有真正缺陷的規範的服務實現客戶端。它像SOAP一樣,雖然它沒有WSDL或等效文件。該規範也沒有提供關於元素正確排序的任何信息 - 它們在規範中按字母順序列出,但如果服務在請求中出現亂序,則服務返回XML解析錯誤(所述順序由檢查例子)。反序列化具有未知元素順序的XML

我可以工作用於提交請求,即使這是一個痛苦。但是,我不知道如何正確處理回覆。

對於SoapEnvelope和直接與XmlSerializer,如果響應包含我尚未正確排序的元素,它將顯示爲我的對象上的null。再一次,我可以設法處理這個問題,並手動排序類屬性with Order attributes,但我無法判斷原始XML是否有一個我沒有正確命令的字段,因此將其保留爲null。如何檢查XmlSerializer是否刪除了一個字段?

+0

我真的希望這實際上是一個X-Y問題,並且有比檢查XmlSerializer更簡單的答案,但我並不期待它。 – Bobson

+1

您可以使用['XmlSerializer.UnknownElement'](https://msdn.microsoft.com/en-us/library/system.xml.serialization.xmlserializer.unknownelement%28v=vs.110%29.aspx)。 – dbc

+0

@dbc - Oooh,可以工作。這並不理想,但我絕對可以用它。當然,這種情況並不理想,所以我不應該感到驚訝。 – Bobson

回答

3

您可以使用XmlSerializer上的XmlSerializer.UnknownElement事件來捕獲亂序元素。這將允許您手動查找並解決反序列化中的問題。

更復雜的答案是在序列化時正確排序元素,但在反序列化時忽略順序。這需要使用XmlAttributes類和XmlSerializer(Type, XmlAttributeOverrides)構造函數。請注意,以這種方式構建的串行器must be cached in a hash table and resused可避免嚴重的內存泄漏,因此此解決方案有點「挑剔」,因爲Microsoft不提供有意義的GetHashCode()用於XmlAttributeOverrides。下面是一個可能實現這取決於事先知道需要他們XmlElementAttribute.OrderXmlArrayAttribute.Order屬性忽略所有類型的,從而避免了需要創建一個複雜的自定義散列方法:

public class XmlSerializerFactory : XmlOrderFreeSerializerFactory 
{ 
    static readonly XmlSerializerFactory instance; 

    // Use a static constructor for lazy initialization. 
    private XmlSerializerFactory() 
     : 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 
    { 
    } 

    static XmlSerializerFactory() 
    { 
     instance = new XmlSerializerFactory(); 
    } 

    public static XmlSerializerFactory Instance { get { return instance; } } 
} 

public abstract class XmlOrderFreeSerializerFactory 
{ 
    readonly XmlAttributeOverrides overrides; 
    readonly object locker = new object(); 
    readonly Dictionary<Type, XmlSerializer> serializers = new Dictionary<Type, XmlSerializer>(); 

    static void AddOverrideAttributes(Type type, XmlAttributeOverrides overrides) 
    { 
     if (type == null || type == typeof(object) || type.IsPrimitive || type == typeof(string)) 
      return; 

     var mask = BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public; 
     foreach (var member in type.GetProperties(mask).Cast<MemberInfo>().Union(type.GetFields(mask))) 
     { 
      XmlAttributes overrideAttr = null; 
      foreach (var attr in member.GetCustomAttributes<XmlElementAttribute>()) 
      { 
       overrideAttr = overrideAttr ?? new XmlAttributes(); 
       overrideAttr.XmlElements.Add(new XmlElementAttribute { DataType = attr.DataType, ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace, Type = attr.Type }); 
      } 
      foreach (var attr in member.GetCustomAttributes<XmlArrayAttribute>()) 
      { 
       overrideAttr = overrideAttr ?? new XmlAttributes(); 
       overrideAttr.XmlArray = new XmlArrayAttribute { ElementName = attr.ElementName, Form = attr.Form, IsNullable = attr.IsNullable, Namespace = attr.Namespace }; 
      } 
      if (overrideAttr != null) 
       overrides.Add(type, member.Name, overrideAttr); 
     } 
    } 

    protected XmlOrderFreeSerializerFactory(IEnumerable<Type> types) 
    { 
     overrides = new XmlAttributeOverrides(); 
     foreach (var type in types.SelectMany(t => t.BaseTypesAndSelf()).Distinct()) 
     { 
      AddOverrideAttributes(type, overrides); 
     } 
    } 

    public XmlSerializer GetSerializer(Type type) 
    { 
     if (type == null) 
      throw new ArgumentNullException("type"); 
     lock (locker) 
     { 
      XmlSerializer serializer; 
      if (!serializers.TryGetValue(type, out serializer)) 
       serializers[type] = serializer = new XmlSerializer(type, overrides); 
      return serializer; 
     } 
    } 
} 

public static class TypeExtensions 
{ 
    public static IEnumerable<Type> BaseTypesAndSelf(this Type type) 
    { 
     while (type != null) 
     { 
      yield return type; 
      type = type.BaseType; 
     } 
    } 
} 

(注 - 只有部分測試。 )

然後,在反序列化類型時,使用工廠提供的XmlSerializer。鑑於SoapEnvelopeXmlDocument的一個子類,您應該能夠按照Deserialize object property with StringReader vs XmlNodeReader中回答的行反序列化主體節點。