2017-02-25 67 views
0

我想序列化一個DataMember屬性的對象在某些屬性上被忽略。自定義DataContractSerializer

說我有自定義屬性MyIgnoreDataMember。

我希望使用它標記的屬性對於我的自定義DataContractSerializer是不可見的,但對於正常的DataContractSerializer可見。我不得不使用DataContractSerializer,而不是其他任何東西。

該代碼是一個Silverlight應用程序。

任何人都已成功完成DataContractSerializer的繼承?

+0

使用序列化代理通常是要走的路 - 但它看起來像在Silverlight上不可用,請參閱http://stackoverflow.com/a/2750121/3744182。 – dbc

+1

在缺少數據合約代理的情況下,您可能會考慮採用不同的方法:使用[此答案](http://stackoverflow.com/a/32150990/3744182)中的'ElementSkippingXmlTextWriter'並在寫入時跳過不需要的元素。 – dbc

+1

唉,['DataContractSerializer'](https://msdn.microsoft.com/en-us/library/system.runtime.serialization.datacontractserializer.aspx)被封了,所以不能被分類。 – dbc

回答

2

你的問題的回答是通過以下問題複雜:

  1. DataContractSerializer是密封的,所以不能被繼承來檢查像MyIgnoreDataMember的屬性。

  2. 使用serialization surrogate注入適當的DTO到序列化過程中你的對象圖通常是要走的路 - 但它看起來像它的不可用Silverlight的,看到this answer

  3. DataContractSerializer不支持ShouldSerialize模式,如解釋here,所以通過回調方法,你不能只是抑制不需要的屬性序列化。

那麼,你有什麼選擇?比方說,你對象圖如下所示:

[DataContract(Name = "Root", Namespace = "http://www.MyNamespace.com")] 
public class RootObject 
{ 
    [DataMember] 
    public NestedObject NestedObject { get; set; } 
} 

[DataContract(Name = "Nested", Namespace = "http://www.MyNamespace.com")] 
public class NestedObject 
{ 
    [DataMember] 
    public string SensitiveData { get; set; } 

    [DataMember] 
    public string PublicData { get; set; } 
} 

而且要有條件地抑制SensitiveData輸出。那麼下面的可能性:

  1. 如果你只需要消除一些屬性,你可以用EmitDefaultValue = false標記它們並返回默認值時,一些線程靜態是true,例如:

    [DataContract(Name = "Root", Namespace = "http://www.MyNamespace.com")] 
    public class RootObject 
    { 
        [DataMember] 
        public NestedObject NestedObject { get; set; } 
    } 
    
    [DataContract(Name = "Nested", Namespace = "http://www.MyNamespace.com")] 
    public class NestedObject 
    { 
        string sensitiveData; 
    
        [DataMember(IsRequired = false, EmitDefaultValue = false)] 
        public string SensitiveData 
        { 
         get 
         { 
          if (SerializationState.InCustomSerialization()) 
           return null; 
          return sensitiveData; 
         } 
         set 
         { 
          sensitiveData = value; 
         } 
        } 
    
        [DataMember] 
        public string PublicData { get; set; } 
    } 
    
    public static class SerializationState 
    { 
        [ThreadStatic] 
        static bool inCustomSerialization; 
    
        public static bool InCustomSerialization() 
        { 
         return inCustomSerialization; 
        } 
    
        public static IDisposable SetInCustomDeserialization(bool value) 
        { 
         return new PushValue<bool>(value,() => inCustomSerialization, b => inCustomSerialization = b); 
        } 
    } 
    
    public struct PushValue<T> : IDisposable 
    { 
        Action<T> setValue; 
        T oldValue; 
    
        public PushValue(T value, Func<T> getValue, Action<T> setValue) 
        { 
         if (getValue == null || setValue == null) 
          throw new ArgumentNullException(); 
         this.setValue = setValue; 
         this.oldValue = getValue(); 
         setValue(value); 
        } 
    
        #region IDisposable Members 
    
        // By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class. 
        public void Dispose() 
        { 
         if (setValue != null) 
          setValue(oldValue); 
        } 
    
        #endregion 
    } 
    

    然後,當序列化,做類似:

    using (SerializationState.SetInCustomDeserialization(true)) 
    { 
        // Serialize with data contract serializer. 
    } 
    

    老實說相當醜陋。

  2. 你可以做一個完整的DTO層次使用相同的協定名稱和命名空間爲您的真實類型,真正的類的DTO的東西,如地圖,和序列化的DTO:

    [DataContract(Name = "Root", Namespace = "http://www.MyNamespace.com")] 
    class RootObjectDTO 
    { 
        [DataMember] 
        public NestedObjectDTO NestedObject { get; set; } 
    } 
    
    [DataContract(Name = "Nested", Namespace = "http://www.MyNamespace.com")] 
    class NestedObjectDTO 
    { 
        [DataMember] 
        public string PublicData { get; set; } 
    } 
    

    如果automapper在silverlight上不可用,因爲合同名稱和命名空間是相同的,您可以使用DataContractSerializer本身來執行映射。即 - 將真正的根對象序列化爲XML字符串(或如下所示的XDocument),將中間XML反序列化爲DTO根,然後序列化出DTO。

  3. 你可以序列化到一個內存中使用以下擴展類XDocument(其is available in silverlight):

    public static partial class DataContractSerializerHelper 
    { 
        public static XDocument SerializeContractToXDocument<T>(this T obj) 
        { 
         return obj.SerializeContractToXDocument(null); 
        } 
    
        public static XDocument SerializeContractToXDocument<T>(this T obj, DataContractSerializer serializer) 
        { 
         var doc = new XDocument(); 
         using (var writer = doc.CreateWriter()) 
         { 
          (serializer ?? new DataContractSerializer(obj.GetType())).WriteObject(writer, obj); 
         } 
         return doc; 
        } 
    
        public static T DeserializeContract<T>(this XDocument doc) 
        { 
         return doc.DeserializeContract<T>(null); 
        } 
    
        public static T DeserializeContract<T>(this XDocument doc, DataContractSerializer serializer) 
        { 
         if (doc == null) 
          throw new ArgumentNullException(); 
         using (var reader = doc.CreateReader()) 
         { 
          return (T)(serializer ?? new DataContractSerializer(typeof(T))).ReadObject(reader); 
         } 
        } 
    } 
    

    接着,使用XPATH queries修剪不需要的元素,然後序列化XDocument到最終的XML表示。

  4. 最後,如果性能和內存使用量很高,可以使用this answer中的ElementSkippingXmlTextWriter在寫入不需要的元素時對其進行修剪。