我希望序列化一个具有DataMember属性的对象,以便在某些属性上被忽略。
假设我有自定义属性MyIgnoreDataMember。
我希望用它标记的属性对于我的自定义DataContractSerializer是不可见的,但对于普通的DataContractSerializer是可见的。
我必须使用DataContractSerializer而不是别的。
代码是Silverlight应用程序。
是否有人成功完成了DataContractSerializer的子类化?
答案 0 :(得分:2)
以下问题使您的问题的答案变得复杂:
DataContractSerializer
已被密封,因此无法进行子类化以检查MyIgnoreDataMember
等属性。
在序列化过程中使用serialization surrogate将适当的DTO注入对象图表通常是可行的方法 - 但看起来它在Silverlight上不可用,请参阅this answer。
DataContractSerializer
不支持ShouldSerialize
模式,如here所述,因此您无法通过回调方法抑制不需要的属性的序列化。< / p>
那么,你有什么选择?让我们说你的对象图如下所示:
[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
的输出。然后是以下几种可能性:
如果您只需要删除一些属性,则可以使用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.
}
老实说非常难看。
您可以使用与真实类型相同的合同名称和名称空间创建整个DTO层次结构,使用类似automapper的内容将实际类映射到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。
您可以使用以下扩展类序列化到内存中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表示。
最后,如果性能和内存使用非常宝贵,您可以使用this answer中的ElementSkippingXmlTextWriter
来删除不需要的元素。