0

I have an object that implements an interface with a property that is another interface:

public interface IMaster
{
   ISlave ObjectProp {get; set; }
}

public interface ISlave 
{
   string Name {get; set; }
}
...
public class Master : IMaster
{
   public ISlave ObjectProp {get; set; }
}
public class Slave : ISlave
{
   public string Name {get; set; }
}

I can create an instance of the Master class and deserialize it using JsonConvert.PopulateObject.
When deserializing the property, however, the parser cannot, obviously, create an instance of an interface.

Is there a way to tell the Json library (via attribute, callback etc.) to create an instance of the Slave class (that implements the interface in question)?

Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78
  • 1
    Possible duplicate of [Using Json.NET converters to deserialize properties](https://stackoverflow.com/q/2254872/10263) or [JSON.NET - how to deserialize collection of interface-instances?](https://stackoverflow.com/q/15880574/10263). If you search on `deserialize interface [Json.Net]` there are many other similar questions. – Brian Rogers Sep 21 '18 at 20:02
  • 1
    Other possible duplicates: [NewtonSoft.Json Serialize and Deserialize class with property of type IEnumerable](https://stackoverflow.com/q/11754633/3744182) (especially [this answer](https://stackoverflow.com/a/11761508/3744182) which looks to answer your question precisely) and [How to implement custom JsonConverter in JSON.NET to deserialize a List of base class objects?](https://stackoverflow.com/q/8030538/3744182). – dbc Sep 21 '18 at 20:37

1 Answers1

1

Yes, this looks like a duplicate - JsonConverterAttribute appears to work fine. For completeness sake, below is the solution:

...
public class Master : IMaster
{
   [JsonConverter(typeof(ModelJsonConverter), typeof(Slave))]
   public ISlave ObjectProp {get; set; }
}
...
public class ModelJsonConverter : JsonConverter
    {
        public Type InstanceType { get; set; }
        public ModelJsonConverter(Type instanceType):base()
        {
            InstanceType = instanceType;
        }

        public override bool CanWrite => false;
        public override bool CanRead => true;
        public override bool CanConvert(Type objectType)
        {
            if (objectType == InstanceType) return true;
            else return InstanceType.GetTypeInfo().Is​Subclass​Of(objectType);
        }

        public override void WriteJson(JsonWriter writer,
                                       object value, JsonSerializer serializer)
        {
            throw new InvalidOperationException("Use default serialization.");
        }

        public override object ReadJson(JsonReader reader,
                                        Type objectType, object existingValue,
                                        JsonSerializer serializer)
        {
            if (reader.TokenType == JsonToken.Null) return null;
            else if (reader.TokenType != JsonToken.StartObject) throw new JsonException($"Unexpected Json token at {reader.Path}: {reader.TokenType.ToString()}");
            var deserialized = Activator.CreateInstance(InstanceType);
            serializer.Populate(reader, deserialized);
            return deserialized;
        }

    }
Dmitry Streblechenko
  • 62,942
  • 4
  • 53
  • 78
  • 1
    Since the type isn't polymorphic you don't actually need to pre-load into a `JObject`. You can populate directly from the incoming `JsonReader reader`. See e.g. the source for [`CustomCreationConverter`](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Converters/CustomCreationConverter.cs). – dbc Sep 21 '18 at 21:11