Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JSON.Net: deserializing polymorphic types without specifying the assembly

Tags:

c#

json.net

I see that using JSON.Net, I can decode polymorphic objects if a $type attribute specifies the specific type of the JSON object. In all the examples I've seen, $type includes the namespace. Is it possible to make this work including just a simple type name without the assembly? I'd be happy to specify a default assembly to the JsonSerializer if that's possible.

I am able to deserialize the JSON using:

public class SingleAssemblyJsonTypeBinder : SerializationBinder
{
    private readonly Assembly _assembly;
    private Dictionary<string, Type> _typesBySimpleName = new Dictionary<string, Type>(StringComparer.OrdinalIgnoreCase); 
    private Dictionary<Type,string> _simpleNameByType = new Dictionary<Type, string>(); 

    public SingleAssemblyJsonTypeBinder(Assembly assembly)
    {
        _assembly = assembly;
        _typesBySimpleName = new Dictionary<string, Type>();

        foreach (var type in _assembly.GetTypes().Where(t => t.IsPublic))
        {
            if (_typesBySimpleName.ContainsKey(type.Name))
                throw new InvalidOperationException("Cannot user PolymorphicBinder on a namespace where multiple public types have same name.");

            _typesBySimpleName[type.Name] = type;
            _simpleNameByType[type] = type.Name;
        }
    }

    public override Type BindToType(string assemblyName, string typeName)
    {
        Type result;
        if (_typesBySimpleName.TryGetValue(typeName.Trim(), out result))
            return result;

        return null;
    }

    public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
    {
        string name;

        if (_simpleNameByType.TryGetValue(serializedType, out name))
        {
            typeName = name;
            assemblyName = null;// _assembly.FullName;
        }
        else
        {
            typeName = null;
            assemblyName = null;
        }
    }
}

...

public static JsonSerializerSettings GetJsonSerializationSettings()
{
    var settings = new JsonSerializerSettings();
    settings.Binder = new SingleAssemblyJsonTypeBinder(typeof(MvcApplication).Assembly);
    settings.TypeNameHandling = TypeNameHandling.Objects;
    return settings;
}

...

var serializer = JsonSerializer.Create(settings);

I haven't been able to make this work with MVC though. I'm configuring JSON deserialization per the code below in Application_Start, and the object is deserialized, but using the base type one.

GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.Binder = new SingleAssemblyJsonTypeBinder(this.GetType().Assembly);
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
GlobalConfiguration.Configuration.Formatters.JsonFormatter.SerializerSettings.TypeNameAssemblyFormat = FormatterAssemblyStyle.Simple;
like image 875
Frank Schwieterman Avatar asked Nov 28 '12 06:11

Frank Schwieterman


1 Answers

Create a derived SerializationBinder in which override BindToName and Set out string assemblyName to null (Edit: or your default assembly name) and out string typeName to your striped type name

Set the binder to the JsonSerializerSettings.Binder before serialisation.

Let me know if this is not working

like image 91
keyr Avatar answered Oct 01 '22 00:10

keyr