2017-02-25 64 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在写入不需要的元素时对其进行修剪。