Home > database >  Deserialization of JSON to List of Interface with generic type parameter
Deserialization of JSON to List of Interface with generic type parameter

Time:10-20

As the title mentions, I am trying to deserialize a JSON but am having some trouble. I think below includes the necessary information.

public class Variable<T> : IVariable where T : IConvertible
{
    //...
}

public class ArrayVariable<T> : IVariable where T : IConvertible
{
    //...
}

So I have a list of IVariable which I then serialize successfully (all of the information is in the json):

JsonConvert.SerializeObject(myIVariableList)

Now I am trying to deserialize it but I am having trouble determining the correct way to go about doing it as it involves finding the generic type T in addition to the type Variable or ArrayVariable. I have already tried

JsonConvert.DeserializeObject<List<IVariable>>(result.newValues)

but obviously, you can create instances of an interface. Any help would be much appreciated.

CodePudding user response:

You can use TypeNameHandling.All to add type information to your serialiazed json and then utilize it during parsing:

var variables = new List<IVariable>()
{
    new Variable<int>()
};
var settings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All };
var serializeObject = JsonConvert.SerializeObject(variables, settings);
var list = JsonConvert.DeserializeObject<List<IVariable>>(serializeObject, settings);

CodePudding user response:

You can use TypeNameHandling.All but I would strongly recommend you avoid it due to it being very dangerous and allows attackers to compromise your code.

Another safer option is to use a custom converter. Here's a very trivial (and fragile) example that should get you started:

First lets make some basic classes that share an interface:

public interface IVariable { }

public class Foo : IVariable
{
    public int A { get; set; }
}

public class Bar : IVariable
{
    public int B { get; set; }
}

Now we can make our converter:

public class IVariableConverter : JsonConverter<IVariable>
{
    public override IVariable ReadJson(JsonReader reader, Type objectType, 
        IVariable existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        // First load the JSON into a JObject
        var variable = JObject.Load(reader);

        // If the JSON had a property called A, it must be a Foo:
        if (variable.ContainsKey("A"))
        {
            return variable.ToObject<Foo>();
        }

        // If the JSON had a property called B, it must be a Bar:
        if (variable.ContainsKey("B"))
        {
            return variable.ToObject<Bar>();
        }

        // And who knows what was passed in if it was missing both of those properties?!
        throw new Exception("Er, no idea what that JSON was supposed to be!");


    }

    public override void WriteJson(JsonWriter writer, IVariable value, 
        JsonSerializer serializer)
    {
        // Feel free to write your own code here if you need it
        throw new NotImplementedException();
    }
}

And now we can do some actual deserialising:

// A basic JSON example:
var json = "[{\"A\":1},{\"B\":2}]";

// The settings to tell the serialiser how to process an IVariable object
var settings = new JsonSerializerSettings
{
    Converters = new List<JsonConverter> { new IVariableConverter() }
};

// And deserialise with the defined settings
var result = JsonConvert.DeserializeObject<List<IVariable>>(json, settings);

You will need to be a bit more creative with how you identify each type, but this is a safe way to achieve what you need.

  • Related