Search code examples
c#arraysjsonjson.netjavascriptserializer

Json: Parse a dynamic type


I'm trying to deserialize some Json:

{
    "name": "foo",
    "value": [ [ 1.2, 2.3, 4.5 ], [ 1.2, 2.3, 4.5 ] ]
}

into this C# class:

class Bar {
    public string name { get; set; }
    public object value { get; set; }
}

value is of type object because it can be a single value or any array of array, of..., of values.

I've tried with the native C# class:

string jsonString = @"{
    ""name"": ""foo"",
    ""value"": [ [ 1.2, 2.3, 4.5 ], [ 1.2, 2.3, 4.5 ] ]
}";
var data1 = new JavaScriptSerializer().Deserialize<Bar>(jsonString).value;

data1 is an object[] of object[] that are in fact decimal. Problem is: I need them to be doubles.

So I've tried with the Json.NET library:

var data2 = JsonConvert.DeserializeObject<Bar>(
    jsonString,
    new JsonSerializerSettings { FloatParseHandling = FloatParseHandling.Double }
).value;

Now the final values are of type double but I lost the structure of arrays of objects, and have instead a JArray of JArray of double.

So my question is: Is it possible to configure the native JavaScriptSerializer class to get doubles instead of decimals or is it possible to make Json.NET return arrays of objects?


Solution

  • As the type of value is not fixed we can say it is dynamic, and we can use the dynamic keyword in C# for that property:

    class Bar
    {
        public string name { get; set; }
        public dynamic value { get; set; }
    }
    

    Here we discover the type of value at runtime and process it accordingly. You are free to stick with the JavaScriptSerializer as I have done here or if you prefer you could look at implementing something similar with Newtonsoft:

    List<double> ParseFoo(string jsonString)
    {
        var data1 = new JavaScriptSerializer().Deserialize<Bar>(jsonString).value;
        var r = new List<double>();
    
        // We can handle a single value, an array, or an array of arrays:
        var array = data1 as object[];
        if (array != null)
        {
            foreach (object obj in array)
            {
                decimal? number = obj as decimal?;
                if (number.HasValue)
                    r.Add((double)number.Value);
                else
                    r.AddRange((obj as object[]).Cast<decimal>().Select(d => (double)d));
            }
        } else
        {
            r.Add((double)data1);
        }
    
        return r;
    }
    

    Testing:

    void Main()
    {
        string jsonString = @"{
        ""name"": ""foo"",
        ""value"": [ [ 1.2, 2.3, 4.5 ], [ 1.2, 2.3, 4.5 ] ]
        }";
    
        Console.WriteLine(ParseFoo(jsonString));
    
        jsonString = @"{
        ""name"": ""foo"",
        ""value"": [ 1.2, 2.3, 4.5 ]
        }";
    
        Console.WriteLine(ParseFoo(jsonString));
    
        jsonString = @"{
        ""name"": ""foo"",
        ""value"": 2.7
        }";
    
        Console.WriteLine(ParseFoo(jsonString));
    }
    

    Console output:

    enter image description here