Reputation: 14972
I have the following class used in a legacy WCF service
[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; }
}
This is passed so a method called AddDocument
to push the document into a store.
At some point in the past I added logging on all our services through a Castle.Windsor interceptor which serialized the data passed to keep a trace of what's passed to the service.
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();
});
}
with the serialization in DumpObject
using the DataContractJsonSerializer
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";
}
}
}
I never was able to serialize the Document
class because it contains a Stream
, and the argument always was serialized as 'NA'. However I would like to know if it is possible to serialize the rest of the object without the stream.
I thought about changing the object type just for serialization, for example by mapping it to another object with a subset of the original object, but this solution doesn't please me since I'm checking and swapping a lot of data for an operation that should be very low key.
Upvotes: 0
Views: 391
Reputation: 2781
This can be done by implementing the IDataContactSurrogate
and providing it to the DataContractJsonSerializer
.
var settings = new DataContractJsonSerializerSettings() { DataContractSurrogate = new SkipStreamSurrogate()};
var serializer = new DataContractJsonSerializer(argument.GetType(), settings);
serializer.WriteObject(ms, argument);
Implementation:
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;
}
}
Upvotes: 1