使用 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;
    }
}