4

I have a dictionary that looks like this that gets Deserialized from a json file to a C# Dictionary

  {
      "Name": "option1",
      "Settings": {
        "setting1": 20,
        "setting2": 2
      }
  }

So I Deserialize this to an object which works but when i try to get that value out of the dictionary it becomes a long instead of an int

        if (!settings.TryGetValue("setting1", out object setting))
        {
            setting = 10;
        }

Then somewhere else I do something like this:

if ((int)setting > 10) {//do something}

Then it gives me the error that an int64 cant be converted to an int32

Deserialization process:

                using (StreamReader reader = new StreamReader(Startup.SettingsPath))
                {
                    SettingsModel settings = JsonConvert.DeserializeObject<SettingsModel>(reader.ReadToEnd()).Applications;

                    return settings

                }

SettingsModel:

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

    public IDictionary<string, object> Settings { get; set; }
}
kjwdamme
  • 289
  • 4
  • 13
  • 14
    I strongly suspect that it's the deserialization process that's giving you an unexpected value here. Please provide a [mcve] showing how you're converting the JSON to a `Dictionary`. – Jon Skeet Jan 18 '19 at 11:51
  • 7
    JSON has no `int` or `long`, just numbers, as opposed to C#'s zoo of types. If you don't specify the types to use when deserializing, the deserialization logic has to guess which type to use. You wanted `int`, it gave you `long`. Either accept that, or consult the documentation on how to make it do something else. (The fact that you have to use `object` here is a sign that there's a probably a better, more specific way to deserialize things.) – Jeroen Mostert Jan 18 '19 at 11:57
  • Ok thanks ill just accept that its a long – kjwdamme Jan 18 '19 at 12:08
  • Ohh okay. what you mean with **actual** json? I provided the json in the first part of the question – kjwdamme Jan 18 '19 at 12:23
  • I believe that in the specific case of JSON.NET, if the number involved is not floating-point (i.e. has no period in the representation) and will not exceed the range of a `long`, it will use `long`, otherwise it will use `BigInteger`. Don't quote me on that, though. And I don't know if it's in the JSON.NET documentation itself; I couldn't immediately find it. Of course, if one of the `settingX` turns out to be `"banana"`, casting to a `long` will fail. If possible, use a typed model where the settings have fixed names and types, rather than a generic `Dictionary`. – Jeroen Mostert Jan 18 '19 at 12:23
  • See dotnetfiddle.net/Y0EQKP from comment in https://stackoverflow.com/questions/35592256/json-net-how-do-i-include-type-name-handling-for-primitive-c-sharp-types-that – user487779 Jul 13 '22 at 11:44

2 Answers2

2

If you really need to deserialize the number as an integer, you could implement a custom JsonConverter as:

public class CustomConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(object).Equals(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        JToken jToken = JValue.ReadFrom(reader);
        switch (reader.TokenType)
        {
            case JsonToken.Integer:
                return jToken.Value<int>();
            case JsonToken.String:
                return jToken.Value<string>();
            // ...and so on...
            default:
                throw new ArgumentException($"Unknown JsonToken: '{reader.TokenType}'.");
        }
    }

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

Pass an instance of the custom converter class to the DeserializeObject() method as:

JsonConvert.DeserializeObject<SettingsModel>(reader.ReadToEnd(), new CustomConverter());
kaffekopp
  • 2,551
  • 6
  • 13
  • I have little experience with JSON.NET, but wouldn't it be more effective to check first if `reader.TokenType` is `JsonToken.Integer`? It makes little sense to pre-emptively try to parse *everything* as an integer -- `TryParse` is relatively expensive. (This code also ignores integer arrays, I think, but that wouldn't be a problem for the JSON as posted since it doesn't have any.) – Jeroen Mostert Jan 18 '19 at 13:14
  • @JeroenMostert You are absolutely right. I have improved my answer. – kaffekopp Jan 18 '19 at 13:26
-1

I had an instance that required a one off for this situation. I implemented the following.

private int GetInt(KeyValuePair<string, object> pair)
{
    object value = pair.Value;
    Type valueType = value.GetType();
    if (property.PropertyType.IsAssignableFrom(typeof(int)))
    {
       value = Convert.ToInt32(value);
    }
}
BinaryPatrick
  • 512
  • 6
  • 16