Search code examples
c#asp.net-web-apiinterfacejsonserializerasp.net-apicontroller

How to Bind a json to an Interface collection, and vice versa within a model using MVC.Net


Update: due the answer, i found old title irrelevant and change it from the 'How to Implement Custom JsonConverter that uses JsonConvert' to what currently it is.


I'm in very tight and time limit situation, and i have to do as what the title says...

i tried many ways till morning, model doesn't bind, the method similar to FormCollection in MVC returns nulls, and so on and so on, since i add a list of interfaces to one of my model, so i need to handle it,... morning client report bugs, and their system is under pressure, so i can't help it but work till it fix.

here's my current code:

What happen? once the writer method returns server returns 500 internal server error...

naturally they do this using serializer object, but i don't know how to do this customization using serializer, so i really in need to call this method which accept a serializer setting.

public class CollectionInterfaceConverterForModels<TInterface> : JsonConverter
{
    private readonly TypeNameSerializationBinder Binder;

    public CollectionInterfaceConverterForModels()
    {
        Binder = new TypeNameSerializationBinder("Cartable.Models.{0}, Cartable");
    }

    public override bool CanConvert(Type objectType)
    {
        return (objectType == typeof(IList<TInterface>));
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        //IList<TInterface> items = serializer.Deserialize<List<TInterface>>(reader).Cast<TInterface>().ToList();
        //return items;
        var json = existingValue.ToString();
        if (json == null)
            json = "";

        return JsonConvert.DeserializeObject<List<TInterface>>(json,
            new JsonSerializerSettings
            {
                TypeNameHandling = TypeNameHandling.Auto,
                Binder = Binder
            });
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //serializer.Serialize(writer, value, typeof(IList<T>));
        string serializeObject = JsonConvert.SerializeObject(value, Formatting.Indented, new JsonSerializerSettings
        {
            TypeNameHandling = TypeNameHandling.Auto,
            Binder = Binder
        });
        writer.WriteRaw(serializeObject);
        writer.Flush();
    }
}

Model sample:

public class IncomingFaxesVM
{
    public long Code { get; set; }
.
.
.

    [JsonConverter(typeof(CollectionInterfaceConverterForModels<IMetadata>))]
    public List<IMetadata> Metadata { get; set; }
}

Solution

  • Yet, i'm unable to do what i asked, but i solved the issue using another way,

    So if you find the answer, i mark yours, other wise i change the title to interface binding so other user can find it easily, and here's my answer to solve the issue.

    public class CollectionInterfaceConverterForModels<TInterface> : JsonConverter
    {
        private readonly TypeNameSerializationBinder Binder;
    
        public CollectionInterfaceConverterForModels()
        {
            Binder = new TypeNameSerializationBinder("Cartable.Models.{0}, Cartable");
        }
    
        public override bool CanConvert(Type objectType)
        {
            return (objectType == typeof(IList<TInterface>));
        }
        public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
        {
            var currentBinder = serializer.Binder;
            var currentTypeNameHandling = serializer.TypeNameHandling;
    
            serializer.TypeNameHandling = TypeNameHandling.Auto;
            serializer.Binder = Binder;
    
            IList<TInterface> items = serializer.Deserialize<List<TInterface>>(reader).ToList();
    
            serializer.TypeNameHandling = currentTypeNameHandling;
            serializer.Binder = currentBinder;
    
            return items;
        }
    
        public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
        {
            //The reason store these, is to leave system unchanged, in case it customized on general section by another programmer
            var currentBinder = serializer.Binder;
            var currentTypeNameHandling = serializer.TypeNameHandling;
    
            serializer.TypeNameHandling=TypeNameHandling.Auto;
            serializer.Binder = Binder;
    
            serializer.Serialize(writer, value, typeof(List<TInterface>));
    
            serializer.TypeNameHandling = currentTypeNameHandling;
            serializer.Binder = currentBinder;
        }
    }