自定义DataContractSerializer

时间:2017-02-25 19:32:15

标签: c# .net silverlight serialization datacontractserializer

我希望序列化一个具有DataMember属性的对象,以便在某些属性上被忽略。

假设我有自定义属性MyIgnoreDataMember。

我希望用它标记的属性对于我的自定义DataContractSerializer是不可见的,但对于普通的DataContractSerializer是可见的。

我必须使用DataContractSerializer而不是别的。

代码是Silverlight应用程序。

是否有人成功完成了DataContractSerializer的子类化?

1 个答案:

答案 0 :(得分:2)

以下问题使您的问题的答案变得复杂:

  1. DataContractSerializer已被密封,因此无法进行子类化以检查MyIgnoreDataMember等属性。

  2. 在序列化过程中使用serialization surrogate将适当的DTO注入对象图表通常是可行的方法 - 但看起来它在Silverlight上不可用,请参阅this answer

  3. DataContractSerializer不支持ShouldSerialize模式,如here所述,因此您无法通过回调方法抑制不需要的属性的序列化。< / p>

  4. 那么,你有什么选择?让我们说你的对象图如下所示:

    [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; }
      }
      

      如果在silverlight上没有automapper,则可以使用DataContractSerializer本身进行映射,因为合约名称和名称空间是相同的。即 - 将真正的根对象序列化为XML字符串(或如下所示的XDocument),将中间XML反序列化为DTO根,然后序列化DTO。

    3. 您可以使用以下扩展类序列化到内存中XDocumentis 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来删除不需要的元素。