Deserialize Collection of Interface-Instances

Deserialize collection of interface-instances?

Bellow full working example with what you want to do:

public interface ITestInterface
{
string Guid { get; set; }
}

public class TestClassThatImplementsTestInterface1 : ITestInterface
{
public string Guid { get; set; }
public string Something1 { get; set; }
}

public class TestClassThatImplementsTestInterface2 : ITestInterface
{
public string Guid { get; set; }
public string Something2 { get; set; }
}

public class ClassToSerializeViaJson
{
public ClassToSerializeViaJson()
{
this.CollectionToSerialize = new List<ITestInterface>();
}
public List<ITestInterface> CollectionToSerialize { get; set; }
}

public class TypeNameSerializationBinder : SerializationBinder
{
public string TypeFormat { get; private set; }

public TypeNameSerializationBinder(string typeFormat)
{
TypeFormat = typeFormat;
}

public override void BindToName(Type serializedType, out string assemblyName, out string typeName)
{
assemblyName = null;
typeName = serializedType.Name;
}

public override Type BindToType(string assemblyName, string typeName)
{
var resolvedTypeName = string.Format(TypeFormat, typeName);
return Type.GetType(resolvedTypeName, true);
}
}

class Program
{
static void Main()
{
var binder = new TypeNameSerializationBinder("ConsoleApplication.{0}, ConsoleApplication");
var toserialize = new ClassToSerializeViaJson();

toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface1()
{
Guid = Guid.NewGuid().ToString(), Something1 = "Some1"
});
toserialize.CollectionToSerialize.Add(
new TestClassThatImplementsTestInterface2()
{
Guid = Guid.NewGuid().ToString(), Something2 = "Some2"
});

string json = JsonConvert.SerializeObject(toserialize, Formatting.Indented,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});
var obj = JsonConvert.DeserializeObject<ClassToSerializeViaJson>(json,
new JsonSerializerSettings
{
TypeNameHandling = TypeNameHandling.Auto,
Binder = binder
});

Console.ReadLine();
}
}

Casting interfaces for deserialization in JSON.NET

@SamualDavis provided a great solution in a related question, which I'll summarize here.

If you have to deserialize a JSON stream into a concrete class that has interface properties, you can include the concrete classes as parameters to a constructor for the class! The NewtonSoft deserializer is smart enough to figure out that it needs to use those concrete classes to deserialize the properties.

Here is an example:

public class Visit : IVisit
{
/// <summary>
/// This constructor is required for the JSON deserializer to be able
/// to identify concrete classes to use when deserializing the interface properties.
/// </summary>
public Visit(MyLocation location, Guest guest)
{
Location = location;
Guest = guest;
}
public long VisitId { get; set; }
public ILocation Location { get; set; }
public DateTime VisitDate { get; set; }
public IGuest Guest { get; set; }
}

Deserialization of JSON to List of Interface with generic type parameter

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.

C# Json.Deserialize with Object with Child class with interfaces

You can create your own JSON converter which allows you to specify type mapping:

public class JsonTypeMapper<TFromType, TToType> : JsonConverter
{
public override bool CanConvert(Type objectType) => objectType == typeof(TFromType);

public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
return serializer.Deserialize<TToType>(reader);
}

public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}

Then you deserialize like this:

JsonConvert.DeserializeObject<MyObject>(myObjStr, new JsonSerializerSettings
{
Converters = new List<JsonConverter> { new JsonTypeMapper<IEntity, Entity>() }
//^^^^^^^, ^^^^^^
});

Json deserialize a complex object with an Interface as a property

It turns out that I can change the property in the TaskDetail from IBehavior to dynamic and it works. Then when I need to use the concrete type I have a factory that can return a concrete type (i.e. torqueTool). At that point I can do

JsonConvert.DeserializeObject<TorqueTool>(taskDetail.Behavior.ToString());


Related Topics



Leave a reply



Submit