183

I am trying to set up a reader that will take in JSON objects from various websites (think information scraping) and translate them into C# objects. I am currently using JSON.NET for the deserialization process. The problem I am running into is that it does not know how to handle interface-level properties in a class. So something of the nature:

public IThingy Thing

Will produce the error:

Could not create an instance of type IThingy. Type is an interface or abstract class and cannot be instantiated.

It is relatively important to have it be an IThingy as opposed to a Thingy since the code I am working on is considered sensitive and unit testing is highly important. Mocking of objects for atomic test scripts is not possible with fully-fledged objects like Thingy. They must be an interface.

I've been poring over JSON.NET's documentation for a while now, and the questions I could find on this site related to this are all from over a year ago. Any help?

Also, if it matters, my app is written in .NET 4.0.

Stephen Kennedy
  • 20,585
  • 22
  • 95
  • 108
tmesser
  • 7,558
  • 2
  • 26
  • 38
  • possible duplicate of [Using Json.NET converters to deserialize properties](http://stackoverflow.com/questions/2254872/using-json-net-converters-to-deserialize-properties) – nawfal Jul 20 '14 at 11:47
  • 2
    I think that approach is flawed. If `Thingy` is a data model, it doesn't matter for unit tests, since it can be initialized and contains no logic and would have the least common nominator of all properties used for your scrapping and adding more specific ones in classes inheriting from it – Tseng Jul 08 '21 at 14:14

17 Answers17

135

@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; }
}
Community
  • 1
  • 1
Mark Meuer
  • 7,200
  • 6
  • 43
  • 64
  • 24
    How would this work with an ICollection? ICollection Guests{get;set;} – DrSammyD Jan 30 '14 at 16:43
  • I have not tried it. Sorry. If you get a chance to check it out, please post here to let us know if it works. – Mark Meuer Jan 31 '14 at 18:30
  • 16
    It works with ICollection, so ICollection works. Just as an FYI, you can put the attribute [JsonConstructor] on your constructor so that it will use that by default if you happen to have multiple constructors – DrSammyD Feb 03 '14 at 17:33
  • 1
    @DrSammyD linq has a shortcut method to cast an IEnumerable and it worked for me. Ex) Guests = guests.Cast(); – jpatapoff Jul 30 '15 at 00:34
  • 9
    I am stuck to the same problem, in my case i have several implementation of the interface ( in your example the interface is ILocation ) so what if there are classes like MyLocation, VIPLocation, OrdinaryLocation. How to map these to Location property ? If you just have one implementation like MyLocation its easy, but how to do it if there are multiple implementations of ILocation ? – ATHER Oct 09 '15 at 23:09
  • @ATHER I have not tried it myself, but it looks like Eric Boumendil's answer may point you towards a solution. Good luck! – Mark Meuer Oct 10 '15 at 16:01
  • The contructor version works fine, but I had to remove the parameterless contructor within the class (or at least change its visibility to 'internal' or similar) to make this work. – Daniel Veihelmann Mar 20 '16 at 21:21
  • 11
    If you have more than one constructor, you can mark up your special constructor with the `[JsonConstructor]` attribute. – Dr Rob Lang May 23 '16 at 12:49
  • 41
    This is not fine at all. The point of using interfaces is to use dependency injection, but doing this with an object typed parameter required by your constructor you totally screw up the point of having an interface as a property. – Jérôme MEVEL Aug 29 '16 at 09:34
  • 1
    This didn't fix my problem, but it did make it GLARINGLY OBVIOUS that I had forgotten to implement an interface on one of the types I was trying to deserialize. Nice trick! – DVK Oct 18 '18 at 18:08
  • 2
    @JérômeMEVEL +1 for you -1 for the answer. It amazes me how many people do things like these. – bokibeg May 29 '19 at 10:04
  • @JérômeMEVEL I have to disagree. This does not have to defeat dependency injection at all. You can also add another constructor that takes the interfaces as arguments, or a default constructor that takes no arguments at all. Then there is no *requirement* that those specific classes be used when instantiating the object. But having this constructor *allows* the deserializer to figure out what classes to use. Also, *one of* the points of having interfaces is dependency injection. That's certainly not the only case in which interfaces are useful. – Mark Meuer May 30 '19 at 13:21
  • 2
    @MarkMeuer - there's more to it than just DI. Interfaces facilitate loose coupling between implementations, allowing many different implementations to be used. ILocation can resolve to a Restaurant, Planet, StreetAddress, or many other types; by constraining the type in the constructor we lose these benefits. It would ideal to be able to deserialize the interface to the concrete type *without* knowing what it is ahead of time, because sometimes we just don't. – Chris Camaratta Jan 12 '20 at 07:08
  • @ChrisCamaratta I almost completely agree. But if you create two constructors, one that accepts concrete classes and one (or more) that accept interfaces then you can have the benefits of both. When trying to deserialize and the concrete classes are not known ahead of time, then you've provided concrete classes for the deserializer to use. But the other constructor allows you to add the object interface arguments if you wish. – Mark Meuer Jan 13 '20 at 18:16
  • 2
    @MarkMeuer Another problem with having a constructor which references concrete types is that you need to be able to reference the assembly which contains those concrete types. If you've defined the interfaces in a separate assembly to avoid this coupling, this undoes that. – Matt Arnold Feb 26 '20 at 10:36
  • @MattArnold I agree. If that is a serious concern then you'll have to write a custom converter or follow one of the suggestions in the other answers. – Mark Meuer Feb 26 '20 at 17:12
  • @MarkMeuer I ended up writing custom converters and duplicating the concrete struct implementations on the deserialization end. In each `ReadJson` implementation I had to remove the converter in question from the incoming serializer before calling `Deserialize` or infinite recursion would occur. I also had to add the converters for any nested complex types on the type about to be deserialized to deserialize them as well. Quite an un-DRY solution which could otherwise probably be automated using reflection and built into JSON.NET. – Matt Arnold Feb 27 '20 at 09:39
  • Does this work for the newer System.Text.JSON? – Brandon Piña Feb 02 '23 at 15:42
  • @BrandonPiña I do not know. I haven't tried or looked into it with the newer libs. – Mark Meuer Feb 02 '23 at 20:05
  • wanna to boost @DrSammyD's question that >How would this work with an ICollection? ICollection Guests{get;set;} I don't think it works with ICollection case. – Ada Jun 29 '23 at 06:46
87

Why use a converter? There is a native functionality in Newtonsoft.Json to solve this exact problem:

Set TypeNameHandling in the JsonSerializerSettings to TypeNameHandling.Auto

JsonConvert.SerializeObject(
  toSerialize,
  new JsonSerializerSettings()
  {
    TypeNameHandling = TypeNameHandling.Auto
  });

This will put every type into the json, that is not held as a concrete instance of a type but as an interface or an abstract class.

Make sure that you are using the same settings for serialization and deserialization.

I tested it, and it works like a charm, even with lists.

Search Results Web result with site links

⚠️ WARNING:

Only use this for json from a known and trusted source. User snipsnipsnip correctly mentioned that this is indeed a vunerability.

See CA2328 and SCS0028 for more information.


Source and an alternative manual implementation: Code Inside Blog

Mafii
  • 7,227
  • 1
  • 35
  • 55
  • 3
    Perfect, this helped me for a quick & dirty deep clone ( http://stackoverflow.com/questions/78536/deep-cloning-objects ) – Christoph Sonntag Mar 24 '17 at 10:53
  • That was it! What's the difference between `Objects` and `Auto` tho? – Shimmy Weitzhandler May 21 '17 at 04:02
  • 1
    @Shimmy Objects: "Include the .NET type name when serializing into a JSON object structure." Auto: Include the .NET type name when the type of the object being serialized is not the same as its declared type. Note that this doesn't include the root serialized object by default. To include the root object's type name in JSON you must specify a root type object with SerializeObject(Object, Type, JsonSerializerSettings) or Serialize(JsonWriter, Object, Type)." Source: http://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_TypeNameHandling.htm – Mafii May 21 '17 at 19:34
  • 1
    Best answer when Types are "green-field" / self controlled. Thanx! – Youp Bernoulli Jul 13 '17 at 13:22
  • 10
    I just tried this on Deserialization and it doesn't work. The subject line of this Stack Overflow question is, "Casting interfaces for deserialization in JSON.NET" – Justin Russo Sep 13 '17 at 13:38
  • 4
    @JustinRusso it only works when the json has been serialized with the same setting – Mafii Sep 13 '17 at 13:40
  • 1
    This one should be the correct answer as it support deserializing an interface into more than one implementation. in my case added this setting also on deserialization function – Gabriel Nov 07 '17 at 20:28
  • 1
    If you use the Newtosoft library this is by far the cleanest approach amongst the proposed ones. – tom33pr Nov 21 '17 at 09:39
  • 3
    Upvote for the quick, if not dirty, solution. If you are just serializing configurations, this works. Beats stopping development to build converters and certainly beats decorating every injected property. serializer.TypeNameHandling = TypeNameHandling.Auto; JsonConvert.DefaultSettings().TypeNameHandling = TypeNameHandling.Auto; – Sean Anderson Dec 20 '17 at 03:32
  • 1
    This is valid and good thing when json data is being serialized and deserialized by same app but if you receive external json then it will not work well – dotnetstep Aug 10 '18 at 16:18
  • Although this worked for me, I ended up just using custom converters to avoid potential security issues which can arise when using non-default values for TypeNameHandling. See here: https://stackoverflow.com/questions/39565954/typenamehandling-caution-in-newtonsoft-json –  Aug 19 '19 at 15:26
  • 2
    @Mafii it doesn't work for me until I change to TypeNameHandling.All. I see that the serialized string does not contain Type information with Auto setting.It's not about deserialize. I am serializing interface object – Hieu Le Jan 09 '20 at 15:11
  • 1
    This method is simple but not recommended if you are receiving JSON from untrusted source: [CA2328](https://learn.microsoft.com/visualstudio/code-quality/ca2328) [SCS0028](https://security-code-scan.github.io/#SCS0028) – snipsnipsnip Feb 18 '20 at 04:47
  • 1
    @snipsnipsnip this is correct. Since this answer has gotten so popular I'll add a warning, thanks for the heads up! – Mafii Feb 18 '20 at 09:23
  • How can an attacker run malicious code in a type when they're only giving you the name of the type, and the definition of the type isn't even on your machine? Am I missing something here? – Kyle Delaney Jun 09 '20 at 21:35
  • 3
    @KyleDelaney AFAIU: 1) Attacks of this kind mostly target the types in the standard library which does some dynamic code execution. 2) Along with typename, JSON content have constructor argument, which is passed to vulnerable classes to be interpreted. See [this Q&A](https://stackoverflow.com/q/39565954/188256) for an example. – snipsnipsnip Jun 11 '20 at 01:28
68

(Copied from this question)

In cases where I have not had control over the incoming JSON (and so cannot ensure that it includes a $type property) I have written a custom converter that just allows you to explicitly specify the concrete type:

public class Model
{
    [JsonConverter(typeof(ConcreteTypeConverter<Something>))]
    public ISomething TheThing { get; set; }
}

This just uses the default serializer implementation from Json.Net whilst explicitly specifying the concrete type.

An overview are available on this blog post. Source code is below:

public class ConcreteTypeConverter<TConcrete> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        //assume we can convert to anything for now
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        //explicitly specify the concrete type we want to create
        return serializer.Deserialize<TConcrete>(reader);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //use the default serialization - it works fine
        serializer.Serialize(writer, value);
    }
}
Community
  • 1
  • 1
Steve Greatrex
  • 15,789
  • 5
  • 59
  • 73
  • 11
    I really like this approach and applied it to our own project. I even added a `ConcreteListTypeConverter` to handle class members of type `IList`. – Oliver Dec 06 '12 at 12:45
  • 4
    That's a great bit of code. It might be nicer to have the actual code for `concreteTypeConverter` in the question though. – Chris Aug 27 '15 at 10:16
  • 3
    @Oliver - Can you post your `ConcreteListTypeConverter` implementation? – Michael Oct 18 '17 at 21:16
  • 3
    And if you have two implementors of ISomething? – bdaniel7 Feb 01 '19 at 15:21
  • For System.Text.Json you can use the same attribute way but a different JsonConverter: https://stackoverflow.com/a/64636093/558211 – Rubenisme Nov 18 '21 at 19:26
64

Use this class, for mapping abstract type, to real type:

public class AbstractConverter<TReal, TAbstract> 
    : JsonConverter where TReal : TAbstract
{
    public override Boolean CanConvert(Type objectType)
        => objectType == typeof(TAbstract);

    public override Object ReadJson(JsonReader reader, Type type, Object value, JsonSerializer jser)
        => jser.Deserialize<TReal>(reader);

    public override void WriteJson(JsonWriter writer, Object value, JsonSerializer jser)
        => jser.Serialize(writer, value);
}

and when deserialize:

var settings = new JsonSerializerSettings
{
    Converters = {
        new AbstractConverter<Thing, IThingy>(),
        new AbstractConverter<Thing2, IThingy2>()
    },
};

JsonConvert.DeserializeObject(json, type, settings);
Simone S.
  • 1,756
  • 17
  • 18
44

To enable deserialization of multiple implementations of interfaces, you can use JsonConverter, but not through an attribute:

Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
serializer.Converters.Add(new DTOJsonConverter());
Interfaces.IEntity entity = serializer.Deserialize(jsonReader);

DTOJsonConverter maps each interface with a concrete implementation:

class DTOJsonConverter : Newtonsoft.Json.JsonConverter
{
    private static readonly string ISCALAR_FULLNAME = typeof(Interfaces.IScalar).FullName;
    private static readonly string IENTITY_FULLNAME = typeof(Interfaces.IEntity).FullName;


    public override bool CanConvert(Type objectType)
    {
        if (objectType.FullName == ISCALAR_FULLNAME
            || objectType.FullName == IENTITY_FULLNAME)
        {
            return true;
        }
        return false;
    }

    public override object ReadJson(Newtonsoft.Json.JsonReader reader, Type objectType, object existingValue, Newtonsoft.Json.JsonSerializer serializer)
    {
        if (objectType.FullName == ISCALAR_FULLNAME)
            return serializer.Deserialize(reader, typeof(DTO.ClientScalar));
        else if (objectType.FullName == IENTITY_FULLNAME)
            return serializer.Deserialize(reader, typeof(DTO.ClientEntity));

        throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
    }

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

DTOJsonConverter is required only for the deserializer. The serialization process is unchanged. The Json object do not need to embed concrete types names.

This SO post offers the same solution one step further with a generic JsonConverter.

Community
  • 1
  • 1
Eric Boumendil
  • 2,318
  • 1
  • 27
  • 32
24

Nicholas Westby provided a great solution in a awesome article.

If you want Deserializing JSON to one of many possible classes that implement an interface like that:

public class Person
{
    public IProfession Profession { get; set; }
}

public interface IProfession
{
    string JobTitle { get; }
}

public class Programming : IProfession
{
    public string JobTitle => "Software Developer";
    public string FavoriteLanguage { get; set; }
}

public class Writing : IProfession
{
    public string JobTitle => "Copywriter";
    public string FavoriteWord { get; set; }
}

public class Samples
{
    public static Person GetProgrammer()
    {
        return new Person()
        {
            Profession = new Programming()
            {
                FavoriteLanguage = "C#"
            }
        };
    }
}

You can use a custom JSON converter:

public class ProfessionConverter : JsonConverter
{
    public override bool CanWrite => false;
    public override bool CanRead => true;
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(IProfession);
    }
    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)
    {
        var jsonObject = JObject.Load(reader);
        var profession = default(IProfession);
        switch (jsonObject["JobTitle"].Value())
        {
            case "Software Developer":
                profession = new Programming();
                break;
            case "Copywriter":
                profession = new Writing();
                break;
        }
        serializer.Populate(jsonObject.CreateReader(), profession);
        return profession;
    }
}

And you will need to decorate the "Profession" property with a JsonConverter attribute to let it know to use your custom converter:

    public class Person
    {
        [JsonConverter(typeof(ProfessionConverter))]
        public IProfession Profession { get; set; }
    }

And then, you can cast your class with an Interface:

Person person = JsonConvert.DeserializeObject<Person>(jsonString);
A. Morel
  • 9,210
  • 4
  • 56
  • 45
12

I found this useful. You might too.

Example Usage

public class Parent
{
    [JsonConverter(typeof(InterfaceConverter<IChildModel, ChildModel>))]
    IChildModel Child { get; set; }
}

Custom Creation Converter

public class InterfaceConverter<TInterface, TConcrete> : CustomCreationConverter<TInterface>
    where TConcrete : TInterface, new()
{
    public override TInterface Create(Type objectType)
    {
        return new TConcrete();
    }
}

Json.NET documentation

smiggleworth
  • 534
  • 4
  • 16
9

Two things you might try:

Implement a try/parse model:

public class Organisation {
  public string Name { get; set; }

  [JsonConverter(typeof(RichDudeConverter))]
  public IPerson Owner { get; set; }
}

public interface IPerson {
  string Name { get; set; }
}

public class Tycoon : IPerson {
  public string Name { get; set; }
}

public class Magnate : IPerson {
  public string Name { get; set; }
  public string IndustryName { get; set; }
}

public class Heir: IPerson {
  public string Name { get; set; }
  public IPerson Benefactor { get; set; }
}

public class RichDudeConverter : JsonConverter
{
  public override bool CanConvert(Type objectType)
  {
    return (objectType == typeof(IPerson));
  }

  public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
  {
    // pseudo-code
    object richDude = serializer.Deserialize<Heir>(reader);

    if (richDude == null)
    {
        richDude = serializer.Deserialize<Magnate>(reader);
    }

    if (richDude == null)
    {
        richDude = serializer.Deserialize<Tycoon>(reader);
    }

    return richDude;
  }

  public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
  {
    // Left as an exercise to the reader :)
    throw new NotImplementedException();
  }
}

Or, if you can do so in your object model, implement a concrete base class between IPerson and your leaf objects, and deserialize to it.

The first can potentially fail at runtime, the second requires changes to your object model and homogenizes the output to the lowest common denominator.

mcw
  • 3,500
  • 1
  • 31
  • 33
  • A try/parse model isn't feasible due to the scale I have to work with. I have to consider a scope of hundreds of base objects with even more hundreds of stub/helper objects to represent embedded JSON objects that happen a lot. It's not out of the question to change the object model, but wouldn't using a concrete base class in the properties make us unable to mock items for unit testing? Or am I getting that backward somehow? – tmesser Apr 25 '11 at 18:28
  • You could still implement a mock from IPerson - note that the type of the Organisation.Owner property is still IPerson. But for deserialization of an arbitrary target you have to return a concrete type. If you don't own the type definition and you can't define the minimum set of properties that your code will require, then your last resort is something like a key/value bag. Using your facebook example comment - can you post in an answer what your (one or multiple) implementations of ILocation look like? That may help move things forward. – mcw Apr 25 '11 at 19:09
  • Since the primary hope is mocking, the ILocation interface is, really, merely a facade for the Location concrete object. A quick example I just worked up would be something like this (http://pastebin.com/mWQtqGnB) for the interface and this (http://pastebin.com/TdJ6cqWV) for the concrete object. – tmesser Apr 26 '11 at 14:56
  • And to go the next step, this is an example of what IPage would look like (http://pastebin.com/iuGifQXp) and Page (http://pastebin.com/ebqLxzvm). The problem, of course, being that while the deserialization of Page would generally work fine, it'll choke when it gets to the ILocation property. – tmesser Apr 26 '11 at 15:02
  • Ok, so thinking about the objects that you're actually scraping and deserializing - is it generally the case that the JSON data is consistent with a single concrete class definition? Meaning (hypothetically) you wouldn't encounter "locations" with additional properties that would make Location unsuitable to use as the concrete type for the deserialized object? If so, attributing the ILocation property of Page with a "LocationConverter" should work. If not, and it's because the JSON data doesn't always conform to a rigid or consistent structure (like ILocation), then (... continued) – mcw Apr 26 '11 at 20:22
  • (... continued) then I need more info. What utility do you want ILocation to provide? Does it represent the "minimum required" fields your system needs? Or just fields you commonly encounter? If it's the latter, and you don't expect every field on every object, have you considered nullable fields or a key/value lookup for storing those fields instead? – mcw Apr 26 '11 at 20:29
  • You mention scale and so what I'm wondering is if your essential problem may be the variability in the structure of the data. What you could also look into, since you are using the 4.0 framework, are dynamic types. Specifically you might want to look at this: http://www.amazedsaint.com/2010/02/introducing-elasticobject-for-net-40.html. You could have Location inherit ElasticObject but still be extensible for additional properties. But you would still need a converter implementation for each concrete class, to get the static interface properties in addition to the dynamic fields. – mcw Apr 26 '11 at 21:01
8

For those that might be curious about the ConcreteListTypeConverter that was referenced by Oliver, here is my attempt:

public class ConcreteListTypeConverter<TInterface, TImplementation> : JsonConverter where TImplementation : TInterface 
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var res = serializer.Deserialize<List<TImplementation>>(reader);
        return res.ConvertAll(x => (TInterface) x);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }
}
Matt M
  • 163
  • 2
  • 6
  • 2
    I'm confused with the overridden `CanConvert(Type objectType) { return true;}`. It seems hacky, how exactly is this helpful? I may be wrong but isn't that like telling a smaller inexperienced fighter that they're going to win the fight no matter the opponent? – Chef_Code May 04 '17 at 20:23
  • Sample usage: `public class Thing : IThingy { [JsonConverter(typeof(ConcreteListTypeConverter))] public List Things }` – tsubasaetkisi Oct 20 '20 at 15:16
5

No object will ever be an IThingy as interfaces are all abstract by definition.

The object you have that was first serialized was of some concrete type, implementing the abstract interface. You need to have this same concrete class revive the serialized data.

The resulting object will then be of some type that implements the abstract interface you are looking for.

From the documentation it follows that you can use

(Thingy)JsonConvert.DeserializeObject(jsonString, typeof(Thingy));

when deserializing to inform JSON.NET about the concrete type.

Sean Kinsey
  • 37,689
  • 7
  • 52
  • 71
  • That is precisely the post from over a year ago that I was referring to. The only major suggestion (writing custom converters) is not terribly feasible with the scale I am forced to consider. JSON.NET has changed a lot in the intervening year. I perfectly understand the distinction between a class and an interface, but C# also supports implicit conversions from an interface to an object that implements the interface with regard to typing. I am essentially asking if there is a way to tell JSON.NET which object will implement this interface. – tmesser Apr 25 '11 at 17:41
  • It was all there in the answer I pointed you to. Make sure there's a `_type` property that signals the concrete type to use. – Sean Kinsey Apr 25 '11 at 17:44
  • And I strongly doubt that C# supports any kind of 'implicit' typecasting from a variable declared as an interface to a concrete type without any sort of hints. – Sean Kinsey Apr 25 '11 at 17:46
  • Unless I read it wrong, the _type property was supposed to be in the JSON to be serialized. That works fine if you're only deserializing what you already serialized, but that's not what's going on here. I am pulling JSON from a number of sites that are not going to be following that standard. – tmesser Apr 25 '11 at 17:49
  • @YYY - Do you control both the serialization to and deserialization from the source JSON? Because ultimately you'll need to either embed the concrete type in the serialized JSON as a hint to use when deserializing or you'll need to use some kind of try/parse model that detects/attempts-to-detect the concrete type at runtime and invoke the appropriate deserializer. – mcw Apr 25 '11 at 17:50
  • @YYY Then just add the _type property to the JSON before you deserialize it - it's a straightforward operation.. And I'm pretty certain that JSON.NET allows for you to specify the concrete `Type` to revive the object with. – Sean Kinsey Apr 25 '11 at 17:55
  • I'm not 100% sure what you mean by 'controlling' the (de)serialization, but our code will be doing both if that's what you're asking. The hurdle is that we will be pulling in a lot of JSON from sites that are unaffiliated with the one I'm building, so in that way we don't get to control it. – tmesser Apr 25 '11 at 17:55
  • @YYY http://james.newtonking.com/projects/json/help/html/M_Newtonsoft_Json_JsonConvert_DeserializeObject_1.htm. As I said, it's easy to specify the _concrete_ type when deserializing. – Sean Kinsey Apr 25 '11 at 17:59
  • @Sean - That is not as simple as you make it out to be since JSON you get into the same problem of having as many converters/JSON doctoring classes as you do objects. The facebook API is a good example of how you can't simply presume an object type by the variable name because multiple objects will use the same name for completely different structures. – tmesser Apr 25 '11 at 18:00
  • @YYY Which is why you have the `_type` hinting... Is it so hard do understand? If you have the name of the interface in the JSON, then have afunction that can inspect it and return the `Type` so that it can be used by `DeserializeObject`. All in all, there are multiple ways of doing this - it's far from difficult. – Sean Kinsey Apr 25 '11 at 18:03
  • @Sean - Just to make sure I am following what you're suggesting, you're recommending setting up individual deserializers on a per-object basis, searching the JSON for these inner objects, and handling them one at a time with JsonConvert.DeserializeObject? – tmesser Apr 25 '11 at 18:03
  • You need a lookup between whatever interface the JSON is marked with and the concrete type now don't you? You don't expect the code to magically infer all of this by it self do you? – Sean Kinsey Apr 25 '11 at 18:09
  • But to get something straight - is this the deserialization of a `property` of an object, that is of type `IThingy`? If that is the case then your question should reflect this as it is completely different matter.. In this case the safest thing would be to just do a `/s/IThingy/Thingy` on the JSON to replace the interfaces with concrete implementations.. – Sean Kinsey Apr 25 '11 at 18:11
  • @Sean - I would appreciate it if you either knocked off the condescending attitude or deleted your answer. I have very clearly spelled out the problems but you seem determined to regard me as an imbecile. This has led to your replies being very unhelpful. _type hinting is unfeasible; I cannot infer types from variable names and have no control over a lot of input. Doing inner static calls per-object may be workable, but also might fall a little short when so many JSON returns are non-standardized. If you are not interested in exploring alternatives and answers, please stop replying. – tmesser Apr 25 '11 at 18:19
  • @ Sean - Yes, this is a property in a class. I felt this was perfectly clear between the 'objects inside a class' clause and the code sample, but I will update the question to clarify. – tmesser Apr 25 '11 at 18:20
  • @YYY Not trying to be condescending, but the `_type` hint _will_ solve it for you, and you seem to be expecting some magic switch that will do everything for you. One option might be to decorate the primary contract with hinting attributes, something like `[ConcreateImpl(typeof(Thingy)] Public IThingy Thingy;`. But: You have a serious problem here - you say you have multiple types of objects and multiple sources you don't control, and hence no way to identify what abstract type any piece of JSON refers to... that means you cannot do this, it's per definition impossible. – Sean Kinsey Apr 25 '11 at 18:28
  • But there is a simple solution - create a Type for deserialization that has only concrete properties - this can either be translated into an Interface-based object, or casted as one depending on the structure of your code. That is if you know that the abstract type needed to revive the data in the first place - which you don't.. – Sean Kinsey Apr 25 '11 at 18:34
  • @Sean - Perhaps I am missing something in your _type suggestion, then. Let's consider an example to make things more clear. Facebook is an easy target so let's try this: https://graph.facebook.com/KState. In this example, the base would be like, "page" or something. My problem is that, for testing purposes, "location" must be something like type ILocation in my code. I must turn the JSON on that page into a "page" object. How am I supposed to handle the _type hint, exactly, given that input? – tmesser Apr 25 '11 at 18:36
  • @YYY See http://www.hanselman.com/blog/SerializingObjectsAsJavaScriptUsingAtlasJSONNETAndAjaxPro.aspx, midway, the part starting with `{"__type":"ConsoleApplication1.Person, ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"` – Sean Kinsey Apr 25 '11 at 18:42
  • Or see http://social.msdn.microsoft.com/Forums/en/asmxandxml/thread/3378b0d6-e221-4d22-81b4-261dcc9191f8 – Sean Kinsey Apr 25 '11 at 18:43
  • Your object can **never** be of _Type_ ILocation - it can be of a Type that _implements_ ILocation. You say you understand the difference between concrete and abstract, but either you do not know the correct terminology, or you just don't. Instantiate an Location and it _will_ adhere to the contract stipulated by ILocation as long as Location implements this interface. – Sean Kinsey Apr 25 '11 at 18:46
4

Suppose an autofac setting like the following:

public class AutofacContractResolver : DefaultContractResolver
{
    private readonly IContainer _container;

    public AutofacContractResolver(IContainer container)
    {
        _container = container;
    }

    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        JsonObjectContract contract = base.CreateObjectContract(objectType);

        // use Autofac to create types that have been registered with it
        if (_container.IsRegistered(objectType))
        {
           contract.DefaultCreator = () => _container.Resolve(objectType);
        }  

        return contract;
    }
}

Then, suppose your class is like this:

public class TaskController
{
    private readonly ITaskRepository _repository;
    private readonly ILogger _logger;

    public TaskController(ITaskRepository repository, ILogger logger)
    {
        _repository = repository;
        _logger = logger;
    }

    public ITaskRepository Repository
    {
        get { return _repository; }
    }

    public ILogger Logger
    {
        get { return _logger; }
    }
}

Therefore, the usage of the resolver in deserialization could be like:

ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<TaskRepository>().As<ITaskRepository>();
builder.RegisterType<TaskController>();
builder.Register(c => new LogService(new DateTime(2000, 12, 12))).As<ILogger>();

IContainer container = builder.Build();

AutofacContractResolver contractResolver = new AutofacContractResolver(container);

string json = @"{
      'Logger': {
        'Level':'Debug'
      }
}";

// ITaskRespository and ILogger constructor parameters are injected by Autofac 
TaskController controller = JsonConvert.DeserializeObject<TaskController>(json, new JsonSerializerSettings
{
    ContractResolver = contractResolver
});

Console.WriteLine(controller.Repository.GetType().Name);

You can see more details in http://www.newtonsoft.com/json/help/html/DeserializeWithDependencyInjection.htm

OmG
  • 18,337
  • 10
  • 57
  • 90
  • I will up-vote this as the best solution. DI has been so widely used this days by c# web devs, and this fit nicely as a centralized place to handle those type conversion by the resolver. – appletwo Jul 26 '17 at 23:56
4

For what it's worth, I ended up having to handle this myself for the most part. Each object has a Deserialize(string jsonStream) method. A few snippets of it:

JObject parsedJson = this.ParseJson(jsonStream);
object thingyObjectJson = (object)parsedJson["thing"];
this.Thing = new Thingy(Convert.ToString(thingyObjectJson));

In this case, new Thingy(string) is a constructor that will call the Deserialize(string jsonStream) method of the appropriate concrete type. This scheme will continue to go downward and downward until you get to the base points that json.NET can just handle.

this.Name = (string)parsedJson["name"];
this.CreatedTime = DateTime.Parse((string)parsedJson["created_time"]);

So on and so forth. This setup allowed me to give json.NET setups it can handle without having to refactor a large part of the library itself or using unwieldy try/parse models that would have bogged down our entire library due to the number of objects involved. It also means that I can effectively handle any json changes on a specific object, and I do not need to worry about everything that object touches. It's by no means the ideal solution, but it works quite well from our unit and integration testing.

tmesser
  • 7,558
  • 2
  • 26
  • 38
3

My solution to this one, which I like because it is nicely general, is as follows:

/// <summary>
/// Automagically convert known interfaces to (specific) concrete classes on deserialisation
/// </summary>
public class WithMocksJsonConverter : JsonConverter
{
    /// <summary>
    /// The interfaces I know how to instantiate mapped to the classes with which I shall instantiate them, as a Dictionary.
    /// </summary>
    private readonly Dictionary<Type,Type> conversions = new Dictionary<Type,Type>() { 
        { typeof(IOne), typeof(MockOne) },
        { typeof(ITwo), typeof(MockTwo) },
        { typeof(IThree), typeof(MockThree) },
        { typeof(IFour), typeof(MockFour) }
    };

    /// <summary>
    /// Can I convert an object of this type?
    /// </summary>
    /// <param name="objectType">The type under consideration</param>
    /// <returns>True if I can convert the type under consideration, else false.</returns>
    public override bool CanConvert(Type objectType)
    {
        return conversions.Keys.Contains(objectType);
    }

    /// <summary>
    /// Attempt to read an object of the specified type from this reader.
    /// </summary>
    /// <param name="reader">The reader from which I read.</param>
    /// <param name="objectType">The type of object I'm trying to read, anticipated to be one I can convert.</param>
    /// <param name="existingValue">The existing value of the object being read.</param>
    /// <param name="serializer">The serializer invoking this request.</param>
    /// <returns>An object of the type into which I convert the specified objectType.</returns>
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        try
        {
            return serializer.Deserialize(reader, this.conversions[objectType]);
        }
        catch (Exception)
        {
            throw new NotSupportedException(string.Format("Type {0} unexpected.", objectType));
        }
    }

    /// <summary>
    /// Not yet implemented.
    /// </summary>
    /// <param name="writer">The writer to which I would write.</param>
    /// <param name="value">The value I am attempting to write.</param>
    /// <param name="serializer">the serializer invoking this request.</param>
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

}

You could obviously and trivially convert it into an even more general converter by adding a constructor which took an argument of type Dictionary<Type,Type> with which to instantiate the conversions instance variable.

Simon Brooke
  • 386
  • 4
  • 7
3

Several years on and I had a similar issue. In my case there were heavily nested interfaces and a preference for generating the concrete classes at runtime so that It would work with a generic class.

I decided to create a proxy class at run time that wraps the object returned by Newtonsoft.

The advantage of this approach is that it does not require a concrete implementation of the class and can handle any depth of nested interfaces automatically. You can see more about it on my blog.

using Castle.DynamicProxy;
using Newtonsoft.Json.Linq;
using System;
using System.Reflection;

namespace LL.Utilities.Std.Json
{
    public static class JObjectExtension
    {
        private static ProxyGenerator _generator = new ProxyGenerator();

        public static dynamic toProxy(this JObject targetObject, Type interfaceType) 
        {
            return _generator.CreateInterfaceProxyWithoutTarget(interfaceType, new JObjectInterceptor(targetObject));
        }

        public static InterfaceType toProxy<InterfaceType>(this JObject targetObject)
        {

            return toProxy(targetObject, typeof(InterfaceType));
        }
    }

    [Serializable]
    public class JObjectInterceptor : IInterceptor
    {
        private JObject _target;

        public JObjectInterceptor(JObject target)
        {
            _target = target;
        }
        public void Intercept(IInvocation invocation)
        {

            var methodName = invocation.Method.Name;
            if(invocation.Method.IsSpecialName && methodName.StartsWith("get_"))
            {
                var returnType = invocation.Method.ReturnType;
                methodName = methodName.Substring(4);

                if (_target == null || _target[methodName] == null)
                {
                    if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
                    {

                        invocation.ReturnValue = null;
                        return;
                    }

                }

                if (returnType.GetTypeInfo().IsPrimitive || returnType.Equals(typeof(string)))
                {
                    invocation.ReturnValue = _target[methodName].ToObject(returnType);
                }
                else
                {
                    invocation.ReturnValue = ((JObject)_target[methodName]).toProxy(returnType);
                }
            }
            else
            {
                throw new NotImplementedException("Only get accessors are implemented in proxy");
            }

        }
    }



}

Usage:

var jObj = JObject.Parse(input);
InterfaceType proxyObject = jObj.toProxy<InterfaceType>();
Sudsy
  • 931
  • 7
  • 16
  • Thanks! This is the only answer the properly supports dynamic typing (duck typing) without forcing restrictions on the incoming json. – Philip Pittle Sep 26 '17 at 00:49
  • No problem. I was a bit surprised to see there was nothing out there. It has moved on a bit since that original example so I decided to share the code. https://github.com/sudsy/JsonDuckTyper . I also published it on nuget as JsonDuckTyper. If you find you want to enhance it, just send me a PR and I'll be happy to oblige. – Sudsy Sep 26 '17 at 20:39
  • When I was looking for a solution in this area I came across https://github.com/ekonbenefits/impromptu-interface also. It doesn't work in my case as it does not support dotnet core 1.0 but it might work for you. – Sudsy Sep 27 '17 at 10:15
  • I did try with Impromptu Interface, but Json.Net wasn't happy doing a `PopulateObject` on the proxy generated by Impromptu Interface. I unfortunately gave up going for Duck Typing - it was just easier to create a custom Json Contract Serializer that used reflection to find an existing implementation of the requested interface and using that. – Philip Pittle Sep 28 '17 at 22:03
1

Use this JsonKnownTypes, it's very similar way to use, it just add discriminator to json:

[JsonConverter(typeof(JsonKnownTypeConverter<Interface1>))]
[JsonKnownType(typeof(MyClass), "myClass")]
public interface Interface1
{  }
public class MyClass : Interface1
{
    public string Something;
}

Now when you serialize object in json will be add "$type" with "myClass" value and it will be use for deserialize

Json:

{"Something":"something", "$type":"derived"}
Dmitry
  • 192
  • 1
  • 3
0

My solution was added the interface elements in the constructor.

public class Customer: ICustomer{
     public Customer(Details details){
          Details = details;
     }

     [JsonProperty("Details",NullValueHnadling = NullValueHandling.Ignore)]
     public IDetails Details {get; set;}
}
Jorge Santos Neill
  • 1,635
  • 13
  • 6
0

You can also use custom TextInputFormatter, no external libraries needed, also helps you gain insight on how you can handle (de)serialization of any type of data.

public class MyInputTypeFormatter : TextInputFormatter
{
    public MyInputTypeFormatter()
    {
        SupportedMediaTypes.Add(MediaTypeHeaderValue.Parse("application/json"));
        SupportedEncodings.Add(Encoding.UTF8);
    }



    protected override bool CanReadType(Type type)
    {
        return type == typeof(MyClass);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
    {
        var httpContext = context.HttpContext;
        var serviceProvider = httpContext.RequestServices;
        var logger = serviceProvider.GetRequiredService<ILogger<ImageTypeConverter>>();
        using var reader = new StreamReader(httpContext.Request.Body, encoding);
        {
            var data = await reader.ReadToEndAsync();
            if (data.Contains("Hello"))
            {
                var myClass= new MyClass(data);
                return await InputFormatterResult.SuccessAsync(myClass);

            }
            else
            {
                return await InputFormatterResult.FailureAsync();

            }
        }


    }
}

Then, simply add this input formatter to the list of input formatters with

services.AddControllers(options=> {
            options.InputFormatters.Insert(0, new MyInputFormatter());
        });

0 here means this is the first input formatter invoked when model binding.

It seems like a lot of work but most of it is just boilerplate. I will explain how this works,

You have an action method/ route which has a parameter of MyClass type. When a request comes to it, your input formatter's CanReadType is invoked and it returns true meaning it will handle the deserialization.Then the ReadRequestBodyAsync method is invoked and the request data is given to it.

You can do whatever you want with the data and return an object of type MyClass if your deserialization succeeds. Else you just return a failure.

In the deserialization you can use

using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
// ...
}

You can traverse elements as the input is parsed into a json object and then held into a DOM. Then you can see what they contain and manually create classes with their data and convert your input-as-interfaces into classes.

Note: JsonDocument was introduced in .Net 3.1 You can check out how to use it here

More about how to use TextInputFormatter and TextOutputFormatter The benefit of using a custom input formatter is that it provides a central class for handling your custom classes which may use multiple interfaces. It also gives you fine control over handling the input data.

Dharman
  • 30,962
  • 25
  • 85
  • 135
cryo
  • 449
  • 3
  • 5