1

I have an object, which contains several dictionary types Which gets serialized to a Json

public class Foo
{
    public Dictionary<string, bool> SomeDict {get;set;}
    public ConcurrentDictionary<string, complextype> SomeconcurrentDict {Get;set;}
}

Now as this answer explains I require a converter to do so. Using that answer as a sample I created the following, so that it could apply to both the dictionary and the concurrentdictionary.

public class JsonDictionaryConverter<k, v> : CustomCreationConverter<IDictionary<k, v>>
{
    public override IDictionary<k, v> Create(Type objectType)
        => Activator.CreateInstance(objectType) as IDictionary<k, v>;

    // in addition to handling IDictionary<string, object>, we want to handle the deserialization of dict value, which is of type object
    public override bool CanConvert(Type objectType) => objectType == typeof(object) || base.CanConvert(objectType);

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject || reader.TokenType == JsonToken.Null)
            return base.ReadJson(reader, objectType, existingValue, serializer);

        // if the next token is not an object
        // then fall back on standard deserializer (strings, numbers etc.)
        return serializer.Deserialize(reader);
    }
}

However due to the as casting to IDictionary<k,v> failing, and resulting in a NULL, instead of an IDictionary, I run into a null reference exception down the line.

The activator create object is however a ConcurrentDictionary, with matching K and V values, which makes me assume that casting it as IDictionary<k,v> should be possible.

So how can the activator instantiated object be cast to a IDictionary<k,v> ?

With the attribute applied, the class looks as following.

public class Foo
{
    [JsonConverter(typeof(JsonDictionaryConverter<string,bool>))]
    public Dictionary<string, bool> SomeDict {get;set;}

    [JsonConverter(typeof(JsonDictionaryConverter<string,complextype>))]
    public ConcurrentDictionary<string, complextype> SomeconcurrentDict {Get;set;}
}

A simplified test scenario on .netfiddler seems to run the code just fine, resulting in nicely created objects.

On my local machine, using a nearly identical object however, this results in the nullrefference exception due to the cast resulting in a NULL

public class Settings 
{
    [JsonConverter(typeof(JsonDictionaryConverter<string,string>))]
    public ConcurrentDictionary<string, string> Prefix { get; set; }

    [JsonConverter(typeof(JsonDictionaryConverter<string,bool>))]
    public ConcurrentDictionary<string, bool> AllowMentions { get; set; }

    public BotSettings()
    {
        Prefix = new ConcurrentDictionary<string, string>();
        AllowMentions = new ConcurrentDictionary<string, bool>();
    }

    public Settings LoadSettings()
    {
        Settings settings = null;

 // Temporary hardcoded json, for debug purposes
        if (File.Exists(file))
            settings = Serializer.DeserializeJson<BotSettings>("{\"Prefix\":{\"164427220197179403\":\".\",\"342638529622310925\":\".\"},\"AllowMentions\":{\"164427220197179403\":true,\"342638529622310925\":true}}");//string.Join(" ", ReadLines(file)));

        return settings;
    }
}
MX D
  • 2,453
  • 4
  • 35
  • 47
  • Are you sure that `k` and `v` are the correct types (that they match the definition in your class)? – Patrick Hofman Aug 28 '17 at 11:28
  • [`IDictionary dict = new ConcurrentDictionary();`](https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/collections/Concurrent/ConcurrentDictionary.cs#L60) ? – mrogal.ski Aug 28 '17 at 11:30
  • @PatrickHofman definitely. and respectively. Also tested it with just a single instance of and the respective concurrentdictionary, but with the same result. – MX D Aug 28 '17 at 11:30
  • Debug, see the type of `objectType`. What is it? I am pretty sure it's not `IDictionary` related. – Renatas M. Aug 28 '17 at 11:37
  • @Reniuz Activator.CreateInstance(objectType).GetType() {System.Collections.Concurrent.ConcurrentDictionary`2[System.String,System.String]} System.Type {System.RuntimeType} . Ran with a . Looks like a ConcurrentDictionary to me, all though type states runtimetype – MX D Aug 28 '17 at 11:42
  • ? You expect or . Something is wrong in your example code. – Renatas M. Aug 28 '17 at 11:46
  • @Reniuz Samples are just fine. Currently have a local test scenario with setup, where as the above examples reflect what I want to create and already found to be failing. – MX D Aug 28 '17 at 11:54
  • 1
    Can you post small complete code that you are testing? – Renatas M. Aug 28 '17 at 11:56
  • WHERE do you get null reference exception? When DEserializing and trying to use the results? If so this is not related with Create method at all - it works. – Renatas M. Aug 28 '17 at 12:09
  • @Reniuz as my question states. During deserialization, the cast results into a NULL. as the cast to IDictionary seems to fail. https://dotnetfiddle.net/u7HGOr however seems to run it just fine. – MX D Aug 28 '17 at 12:12
  • So first thing is that _"this will throw a null reference exception due to the as casting to IDictionary"_ is a huge lie. You can even try to cast `null as IDictionary` and there will be no exception. Second thing is that you're passing the wrong type in to the `Create` method. [example here](http://rextester.com/FSW78857) – mrogal.ski Aug 28 '17 at 12:13
  • @m.rogalski Maybe not phrased in the best way. The failure to cast to an IDictionary (aka resulting NULL) leads to a null reference exception down the road. So remains the question, why the resulting ConcurrentDictionary is not casting towards IDictionary, which it should be able to. – MX D Aug 28 '17 at 12:20
  • First thing, are you 100% percent sure your dictionaries are initiated? – Renatas M. Aug 28 '17 at 12:22
  • @Reniuz That should not matter. As this should happens during deserialization, which in place, should instantiate the dictionary. Which it also does on in the `create`. Where it fails to cast to the expected IDictionary, resulting in a null. So no, it's not instantiating, thats what the whole question is about.. – MX D Aug 28 '17 at 12:24
  • 1
    The thing is, exception is not related with the code you posted. We have no idea where and how this class is used. Tests of class works. – Renatas M. Aug 28 '17 at 12:36
  • @MXD Just debug on what type is going in to your `Create` method. I doubt that it's even possible to get null by calling `ConcurrentDictionary as IDictionary`. There has to be an issue with your `Type` parameter – mrogal.ski Aug 28 '17 at 13:16
  • 1
    If it's an error for `Create` to return `null` then change your use of `as` to an explicit cast i.e. `(IDictionary)Activator.CreateInstance(objectType)`. That will also simplify the debugging process. – Lee Aug 28 '17 at 13:27

1 Answers1

1

Turns out it was not the Create causing the exception. But the method calling Create instead:

return base.ReadJson(reader, objectType, existingValue, serializer);

This seemed to be an issue in the older version of NewtonSoft, which got resolved by upgrading to the newest version 10.0.3. This was also the reason the Fiddle did work correctly, as this did use the newer version, as opposed to my own project.

MX D
  • 2,453
  • 4
  • 35
  • 47