13

I managed to get my self into a fix with the JSON.net TypeNameHandling. I am storing a JSON formatted object using RavenDB and set the TypeNameHandling of the JSON.net serializer to true in order to deal with an inheritance structure I have in place.

I needed to change the namespace of the document which I am storing, so now when it is deserialzed it is throws the error "Error resolving type specified in JSON" because the reference to the type in the JSON document no longer exists.

Is it possible to intercept the Json deserialization in order to do some kind of rolling migration?

Thanks,

Ross Jones
  • 973
  • 7
  • 20

2 Answers2

22

Ok, figured it out. In the end it was pretty straight forward. You need to override the DefaultSerializationBinder which is responsible for creating the .Net type from the document. Since my json document has the old namespace in it, I needed to intercept the creation of that type to return the correct type. I put together a simple implementation which will allow you to configure "migrations" when the JSON serializer is created.

    public class NamespaceMigrationSerializationBinder : DefaultSerializationBinder
    {
        private readonly INamespaceMigration[] _migrations;

        public NamespaceMigrationSerializationBinder(params INamespaceMigration[] migrations)
        {
            _migrations = migrations;
        }

        public override Type BindToType(string assemblyName, string typeName)
        {
            var migration = _migrations.SingleOrDefault(p => p.FromAssembly == assemblyName && p.FromType == typeName);
            if(migration != null)
            {
                return migration.ToType;
            }
            return base.BindToType(assemblyName, typeName);
        }
    }

Where the interface is

public interface INamespaceMigration
{
    string FromAssembly { get; }

    string FromType { get; }

    Type ToType { get; }
}
Mrchief
  • 75,126
  • 20
  • 142
  • 189
Ross Jones
  • 973
  • 7
  • 20
  • 3
    for those wondering how to hook it up, it's like this: documentStore.Conventions.CustomizeJsonSerializer = serializer => serializer.Binder = new NamespaceMigrationSerializationBinder(migrations); – NeilD Dec 16 '13 at 11:17
  • I think you may want to override BindToName too, or your saves may have a $type="". You could do something like: public override void BindToName(Type serializedType, out string assemblyName, out string typeName) { typeName = serializedType.FullName; assemblyName = serializedType.Assembly.FullName; – pomarc Dec 27 '17 at 16:47
0

You can use an DocumentConversionListener to do this. Please take a look here: http://ayende.com/blog/66563/ravendb-migrations-rolling-updates

Daniel Lang
  • 6,819
  • 4
  • 28
  • 54
  • Thanks Daniel. But the problem occurs before it is able to get here. If you look at the interface of IDocumentConversionListener you will see it is sending the deserializded object through. My problem is that it is failing on deserialization. – Ross Jones Mar 28 '12 at 19:55
  • You have a parameter 'document' of type RavenJObject, which is the json tree before it is deserialized. This should enable you to change the value of the property before it is deserialized into the object/property. Hope that helps - if not, please post some code. – Daniel Lang Mar 29 '12 at 05:37