gregseth
gregseth

Reputation: 13408

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?

Upvotes: 1

Views: 2011

Answers (2)

Stephen Kennedy
Stephen Kennedy

Reputation: 21548

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

Upvotes: 2

Vidmantas Blazevicius
Vidmantas Blazevicius

Reputation: 4802

If your Json for Value property is always an array (even if the single value is an array) - then you can always deserialize into object[] for that property. That will handle arrays of arrays as well.

    {"name": "foo","value": [1.2]};

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

If that is not the case and it can be either a value on its own or array or array of arrays then you might need to resort to implementing custom JsonConverter to account for your business requirements.

https://www.newtonsoft.com/json/help/html/CustomJsonConverter.htm

Upvotes: 0

Related Questions