使用 DataContractJsonSerializer 进行部分序列化
Partial serialization with DataContractJsonSerializer
我在遗留 WCF 服务中使用了以下 class
[MessageContract()]
public class Document
{
[MessageHeader(MustUnderstand = true)]
public MetaData Data { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Name { get; set; }
[MessageBodyMember(Order = 1)]
public Stream File { get; set; }
}
这是通过一个名为 AddDocument
的方法将文档推送到商店。
在过去的某个时候,我通过一个 Castle.Windsor 拦截器添加了我们所有服务的日志记录,该拦截器序列化传递的数据以跟踪传递给服务的内容。
public void Intercept(IInvocation invocation)
{
Logger.Debug(() =>
{
StringBuilder sb = new StringBuilder(1000);
sb.AppendFormat("{2} -> {0}.{1}(", invocation.TargetType.Name, invocation.Method.Name, SomeCode);
sb.Append(string.Join(", ", invocation.Arguments.Select(a => a == null ? "null" : DumpObject(a)).ToArray()));
sb.Append(")");
return sb.ToString();
});
invocation.Proceed();
Logger.Debug(() =>
{
StringBuilder sb = new StringBuilder(1000);
sb.AppendFormat("OUT {0}", invocation.ReturnValue != null ? DumpObject(invocation.ReturnValue) : "void");
return sb.ToString();
});
}
使用 DataContractJsonSerializer
在 DumpObject
中进行序列化
private string DumpObject(object argument)
{
using (var ms = new MemoryStream())
{
try
{
var ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(argument.GetType());
ser.WriteObject(ms, argument);
return System.Text.Encoding.UTF8.GetString(ms.GetBuffer(), 0, Convert.ToInt32(ms.Length));
}
catch (Exception)
{
return "NA";
}
}
}
我从未能够序列化 Document
class,因为它包含一个 Stream
,并且参数总是被序列化为 'NA'。但是我想知道是否可以在没有流的情况下序列化对象的其余部分。
我考虑过更改对象类型只是为了序列化,例如通过将其映射到另一个具有原始对象子集的对象,但这个解决方案并不令我满意,因为我正在检查和交换很多一个应该非常低调的操作的数据。
这可以通过实施 IDataContactSurrogate
并将其提供给 DataContractJsonSerializer
来完成。
var settings = new DataContractJsonSerializerSettings() { DataContractSurrogate = new SkipStreamSurrogate()};
var serializer = new DataContractJsonSerializer(argument.GetType(), settings);
serializer.WriteObject(ms, argument);
实施:
public sealed class SkipStreamSurrogate : IDataContractSurrogate
{
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public Type GetDataContractType(Type type)
{
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// Skip serialization of a System.Stream
if (obj is Stream)
{ return null; }
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null;
}
public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
{
return typeDeclaration;
}
}
我在遗留 WCF 服务中使用了以下 class
[MessageContract()]
public class Document
{
[MessageHeader(MustUnderstand = true)]
public MetaData Data { get; set; }
[MessageHeader(MustUnderstand = true)]
public string Name { get; set; }
[MessageBodyMember(Order = 1)]
public Stream File { get; set; }
}
这是通过一个名为 AddDocument
的方法将文档推送到商店。
在过去的某个时候,我通过一个 Castle.Windsor 拦截器添加了我们所有服务的日志记录,该拦截器序列化传递的数据以跟踪传递给服务的内容。
public void Intercept(IInvocation invocation)
{
Logger.Debug(() =>
{
StringBuilder sb = new StringBuilder(1000);
sb.AppendFormat("{2} -> {0}.{1}(", invocation.TargetType.Name, invocation.Method.Name, SomeCode);
sb.Append(string.Join(", ", invocation.Arguments.Select(a => a == null ? "null" : DumpObject(a)).ToArray()));
sb.Append(")");
return sb.ToString();
});
invocation.Proceed();
Logger.Debug(() =>
{
StringBuilder sb = new StringBuilder(1000);
sb.AppendFormat("OUT {0}", invocation.ReturnValue != null ? DumpObject(invocation.ReturnValue) : "void");
return sb.ToString();
});
}
使用 DataContractJsonSerializer
DumpObject
中进行序列化
private string DumpObject(object argument)
{
using (var ms = new MemoryStream())
{
try
{
var ser = new System.Runtime.Serialization.Json.DataContractJsonSerializer(argument.GetType());
ser.WriteObject(ms, argument);
return System.Text.Encoding.UTF8.GetString(ms.GetBuffer(), 0, Convert.ToInt32(ms.Length));
}
catch (Exception)
{
return "NA";
}
}
}
我从未能够序列化 Document
class,因为它包含一个 Stream
,并且参数总是被序列化为 'NA'。但是我想知道是否可以在没有流的情况下序列化对象的其余部分。
我考虑过更改对象类型只是为了序列化,例如通过将其映射到另一个具有原始对象子集的对象,但这个解决方案并不令我满意,因为我正在检查和交换很多一个应该非常低调的操作的数据。
这可以通过实施 IDataContactSurrogate
并将其提供给 DataContractJsonSerializer
来完成。
var settings = new DataContractJsonSerializerSettings() { DataContractSurrogate = new SkipStreamSurrogate()};
var serializer = new DataContractJsonSerializer(argument.GetType(), settings);
serializer.WriteObject(ms, argument);
实施:
public sealed class SkipStreamSurrogate : IDataContractSurrogate
{
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
return null;
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
return null;
}
public Type GetDataContractType(Type type)
{
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
return obj;
}
public void GetKnownCustomDataTypes(Collection<Type> customDataTypes)
{
}
public object GetObjectToSerialize(object obj, Type targetType)
{
// Skip serialization of a System.Stream
if (obj is Stream)
{ return null; }
return obj;
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
return null;
}
public CodeTypeDeclaration ProcessImportedType(CodeTypeDeclaration typeDeclaration, CodeCompileUnit compileUnit)
{
return typeDeclaration;
}
}