4

I have a single level json that I want to deserialize into a Dictionary<string,object> using Json.Net.

The dictionary's value can be a primitive, string or a (primitive\string) array.

The deserialization knows how to handle primitives and strings, but when it gets to an array of primitives it deserializes it into a JArray (instead of a primitive array).

Here's a small code example of what I mean:

string jsonStr = @"{""obj"": 7, ""arr"": ['1','2','3']}"; 
Dictionary<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonStr);

dict["obj"].GetType(); // long
dict["arr"].GetType(); // JArray. I would like this to be string[].

I'm looking for a way I can interfere in the deserialization process and create a primitive array instead of getting stuck with a JArray.

I've tried doing it with the JsonSerializerSettings, but couldn't nail the spot.

Amir Popovich
  • 29,350
  • 9
  • 53
  • 99
  • 1
    See [How do I use JSON.NET to deserialize into nested/recursive Dictionary and List?](http://stackoverflow.com/q/5546142/10263) – Brian Rogers Jul 26 '16 at 17:55

2 Answers2

1

Here is a hacky way of doing this. I create a custom JsonConverter class which accepts 2 generic arguments: T1 and T2. T1 gives the type of the array (in this case string) and T2 gives the type of the other object (in this case long). I assume that we basically want a Dictionary, but that part could definitely be improved.

using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            string jsonStr = @"{""obj"": 7, ""arr"": ['1','2','3']}";
            Dictionary<string, object> dict = JsonConvert.DeserializeObject<Dictionary<string, object>>(jsonStr, new SpecialConverter<string, long>());

            dict["obj"].GetType(); // long
            dict["arr"].GetType(); // string[].
        }

        class SpecialConverter<T1, T2> : JsonConverter
        {
            public override bool CanConvert(Type objectType)
            {
                return true;
            }

            public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
            {
                JToken token = JToken.Load(reader);
                var result = new Dictionary<string, object>();
                if (result.GetType() == objectType)
                {
                    foreach (var item in token)
                    {
                        var prop = (JProperty)item;
                        if (prop.Value.Type == JTokenType.Array)
                        {
                            result.Add(prop.Name, prop.Value.ToObject<T1[]>());
                        }
                        else
                        {
                            result.Add(prop.Name, prop.Value.ToObject<T2>());
                        }
                    }

                    return result;
                }
                else
                {
                    return null;
                }
            }

            public override bool CanWrite
            {
                get { return false; }
            }

            public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
            {
                throw new NotImplementedException();
            }
        }
    }
}
Ringil
  • 6,277
  • 2
  • 23
  • 37
  • My object is large and complex. Your solution is actually to re-write the serializer. +1 for the effort, I was looking for something a bit more generic and not to re-write the json serializer. – Amir Popovich Jul 27 '16 at 08:08
  • @AmirPopovich I don't think you can do much better than `object []` for something generic and huge unless you were to make classes for your JSON objects with something like http://json2csharp.com/. – Ringil Jul 27 '16 at 10:47
0

The reason it does not convert into an array is because you are telling JsonConvert to convert it to an Dictionary<string, object> and that is exactly what its doing based on the fact that it hasn't got a specific Object to convert to , hence its has "guess" picked JArray, If you can have multiple types for the dictionary's value, best thing would be to convert it to dynamic object. This will give you an array if the value of "arr" is array or a string if the value is a simple string.

string jsonStr = @"{""obj"": 7, ""arr"": ['1','2','3']}";
        dynamic dict = JsonConvert.DeserializeObject(jsonStr);
        var arr = dict.arr;
        string firstelement = arr[0]; // print "1"
Preet Singh
  • 1,791
  • 13
  • 16
  • I understand the reason it doesn't convert it the way I want it to. I'm trying to somehow interfere (maybe somehow via a Converter) in order to make it happen. dynamic is not an option since it will still keep it as a JArray. Thanks anyway! – Amir Popovich Jul 27 '16 at 08:03
  • Dynamic does not keep it as an JArray, it converts it to an array. – Preet Singh Jul 27 '16 at 08:05
  • Yes it does. I just checked it to be 100% sure. This line fo code returns true: `dict["arr"].GetType() == typeof(JArray)` – Amir Popovich Jul 27 '16 at 08:10
  • Apologies yes you are right, when you step over var arr = dict.arr; the type is array, but when you do get type it comes out to be JArray. – Preet Singh Jul 27 '16 at 09:09