views:

1651

answers:

5

Hi there

Is anybody using JSON.NET with nHibernate? I notice that I'm getting errors when i try to load a class with child collections.

Thanks

Graham

+1  A: 

Are you getting a circular dependancy-error? How do you ignore objects from serialization?

Since lazy loading generates a proxy-objects, any attributes your class-members have will be lost. I ran into the same issue with Newtonsoft JSON-serializer, since the proxy-object didn't have the [JsonIgnore] attributes anymore.

jishi
See mine and Handcraftsman's replies, they contain a solution for exactly this problem.
Liedman
Kind of retarded to down vote an answer that was written 1,5 years before yours?
jishi
A: 

Yes that's exactly the issue I was running into. I hadn't been ignoring any objects from serialization when I was getting the errors though. I think I need to go back and read the docs properly!

Please write comments like these as a comment to the reply you're answering, rather than a new answer. It's hard to understand what you are replying to.Replies should only be used for things that actually help to solve the question.
Liedman
+2  A: 

You will probably want to eager load most of the object so that it can be serialized:

        ICriteria ic = _session.CreateCriteria(typeof(Person));

        ic.Add(Restrictions.Eq("Id", id));

        if (fetchEager)
        {
            ic.SetFetchMode("Person", FetchMode.Eager);
        }

A nice way to do this is to add a bool to the constructor (bool isFetchEager) of your data provider method.

David P
+5  A: 

I use NHibernate with Json.NET and noticed that I was getting inexplicable "__interceptors" properties in my serialized objects. A google search turned up this excellent solution by Lee Henson which I adapted to work with Json.NET 3.5 Release 5 as follows.

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

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

    members.RemoveAll(memberInfo =>
                      (IsMemberPartOfNHibernateProxyInterface(memberInfo)) ||
                      (IsMemberDynamicProxyMixin(memberInfo)) ||
                      (IsMemberMarkedWithIgnoreAttribute(memberInfo, objectType)) ||
                      (IsMemberInheritedFromProxySuperclass(memberInfo, objectType)));

    var actualMemberInfos = new List<MemberInfo>();

    foreach (var memberInfo in members)
    {
      var infos = memberInfo.DeclaringType.BaseType.GetMember(memberInfo.Name);
      actualMemberInfos.Add(infos.Length == 0 ? memberInfo : infos[0]);
    }

    return actualMemberInfos;
  }

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

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

  private static bool IsMemberMarkedWithIgnoreAttribute(MemberInfo memberInfo, Type objectType)
  {
    var infos = typeof(INHibernateProxy).IsAssignableFrom(objectType)
                  ? objectType.BaseType.GetMember(memberInfo.Name)
                  : objectType.GetMember(memberInfo.Name);

    return infos[0].GetCustomAttributes(typeof(JsonIgnoreAttribute), true).Length > 0;
  }

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

To use it just put an instance in the ContractResolver property of your JsonSerializer. The circular dependency problem noted by jishi can be resolved by setting the ReferenceLoopHandling property to ReferenceLoopHandling.Ignore . Here's an extension method that can be used to serialize objects using Json.Net

  public static void SerializeToJsonFile<T>(this T itemToSerialize, string filePath)
  {
    using (StreamWriter streamWriter = new StreamWriter(filePath))
    {
      using (JsonWriter jsonWriter = new JsonTextWriter(streamWriter))
      {
        jsonWriter.Formatting = Formatting.Indented;
        JsonSerializer serializer = new JsonSerializer
          {
            NullValueHandling = NullValueHandling.Ignore,
            ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
            ContractResolver = new NHibernateContractResolver(),
          };
        serializer.Serialize(jsonWriter, itemToSerialize);
      }
    }
  }
Handcraftsman
Thanks for this code, it works great! And saved me from using a StatelessSession.
Daniel T.
This stopped working in JSON.NET 3.5 release 7, but still works fine in 3.5 release 5.
zcrar70
they seem to have updated the AssemblyVersion as of release 6, meaning that if you previously included this file in your solution, the version would mismatch, and throw some sort of securityexception. Could that be the case?
jishi
+2  A: 

We had this exact problem, which was solved with inspiration from Handcraftsman's response here.

The problem arises from JSON.NET being confused about how to serialize NHibernate's proxy classes. Solution: serialize the proxy instances like their base class.

A simplified version of Handcraftsman's code goes like this:

public class NHibernateContractResolver : DefaultContractResolver {
    protected override List<MemberInfo> GetSerializableMembers(Type objectType) {
        if (typeof(INHibernateProxy).IsAssignableFrom(objectType)) {
            return base.GetSerializableMembers(objectType.BaseType);
        } else {
            return base.GetSerializableMembers(objectType);
        }
    }
}

IMHO, this code has the advantage of still relying on JSON.NET's default behaviour regarding custom attributes, etc. (and the code is a lot shorter!).

Liedman
Worked great for me!
Daniel T.