Serializing proxied types

Nov 22, 2007 at 2:15 PM
Hi

I'm using Active Record / MonoRail / Json.Net on a project at the moment, and it was all going swimmingly until I attempted to serialize a collection containing a proxy object. If you are not familiar with AR/NHibernate, a proxy object is created via Castle.DynamicProxy2 for ActiveRecord types to allow things like lazy loading of collections/referenced objects. I receive a JsonSerializationException via the AddMapping method on the MemberMappingCollection, because the way the proxy (a subclass of my AR type) overrides the public properties means that GetProperties(flags) returns duplicate entries (one with declaring type of the proxy, one with declaring type of the original class). See discussion here for more details: http://groups.google.com/group/castle-project-devel/browse_thread/thread/2fa34f49d280f230

I have a patch which amends the AddMapping logic to allow a MappingName to be added several times for the same inheritance chain. If a mapping is found for a superclass of the mapping currently held in the MemberMappingCollection, it is ignored. If a mapping is found which is in a subclass, it replaces the currently stored mapping. How does that sound to you?

Cheers
Lee
Nov 23, 2007 at 10:04 AM
Actually I've just uploaded what I think is a superior solution to my problem. Instead of making those changes to MemberMappingCollection, I've extracted the code that obtains the list of members into an overridable method. Then I can use a specialized JsonConverter:

public class NHibernateProxyJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(INHibernateProxy).IsAssignableFrom(objectType);
}

public override void WriteJson(JsonWriter writer, object value)
{
NHibernateProxyJsonSerializer serializer = new NHibernateProxyJsonSerializer();
serializer.Serialize(writer, value);
}
}

And a specialized JsonSerializer:

public class NHibernateProxyJsonSerializer : JsonSerializer
{
private static readonly MemberInfo[] NHibernateProxyInterfaceMembers = typeof(INHibernateProxy).GetMembers();

protected override List<MemberInfo> GetSerializableMembers(Type objectType)
{
List<MemberInfo> members = base.GetSerializableMembers(objectType);

members.RemoveAll(delegate(MemberInfo memberInfo)
{
// We only want to serialize those members which are declared on the proxy, and are not part of the
// INHibernateProxy interface or mixed-in by DynamicProxy.
return
(IsMemberPartOfNHibernateProxyInterface(memberInfo)) ||
(IsMemberDynamicProxyMixin(memberInfo)) ||
(IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
(IsMemberInheritedFromProxySuperclass(memberInfo, objectType));
});

return members;
}

private static bool IsMemberPartOfNHibernateProxyInterface(MemberInfo memberInfo)
{
return Array.Exists(NHibernateProxyInterfaceMembers, delegate(MemberInfo mi) { return memberInfo.Name == mi.Name; });
}

private static bool IsMemberDynamicProxyMixin(MemberInfo memberInfo)
{
return memberInfo.Name == "__interceptors";
}

private static bool IsMemberMarkedWithIgnoreAttribute(MemberInfo memberInfo, Type objectType)
{
return objectType.BaseType.GetMember(memberInfo.Name)0.GetCustomAttributes(typeof (JsonIgnoreAttribute), true).Length > 0;
}

private static bool IsMemberInheritedFromProxySuperclass(MemberInfo memberInfo, Type objectType)
{
return memberInfo.DeclaringType != objectType;
}
}

I've also tweaked the exception thrown when a self-referencing loop is encountered: the message now returns the current state of the stack, so it is easier to determine the route the loop is taking.
Coordinator
Feb 8, 2008 at 11:27 PM
Hey good stuff.

I would rather not add that converter directly to the Json.NET source because then a reference to NHibernate would be needed, but it is good now that if anyone else needs to serialize these objects they can find the solution here.
Mar 3, 2008 at 12:23 PM
Hi James,

The patch I uploaded doesn't have anything to do with NHibernate. It simply allows you to supply a different strategy for choosing which members on an object are serialized. The NHibernate stuff is merely a possible usage. It would be great if you could take another look.

Cheers
Lee
Apr 29, 2010 at 12:09 PM

BTW, for anyone looking into this - this Stack Overflow question explains how to do this in JSON.NET 3.5: http://stackoverflow.com/questions/286721 (the extract Lee posted above is for an earlier version of JSON.NET.)

Note that the Stack Overflow method stopped working in 3.5 release 7, but seems to work fine in release 5 - I'm not sure whether that's due to a regression or a planned change in the internal API.

Jul 15, 2010 at 1:52 PM
I'd hate to be a "bumper" but can someone please look at this issue, and possibly integrating Leem's patch?