Todd Menier Todd Menier - 3 months ago 30
JSON Question

Json.NET - deserialize directly from a stream to a dynamic?

With a little help from the performance tips in the Json.NET docs, I put together a method for downloading/deserializing JSON from a remote resource:

public async Task<T> GetJsonAsync<T>(string url)
{
using (var stream = await new HttpClient().GetStreamAsync(url))
{
using (var sr = new StreamReader(stream))
{
using (var jr = new JsonTextReader(sr))
{
return new JsonSerializer().Deserialize<T>(jr);
}
}
}
}


I'd like to have a non-generic version that returns a
dynamic
. Calling the above method with
GetJsonAsync<dynamic>(url)
works, until you try to access a dynamic property on the result, at which point I get:

'Newtonsoft.Json.Linq.JObject' does not contain a definition for '[MyProperty]'


I have seen how to deserialize to a dynamic from a string, but have not seen a working example of doing it directly from a stream, which would be preferable as it is more memory efficient. Is this possible?

Answer

It turns out this had little to do with Json.NET and more to do with my understanding of dynamics (which I rarely use). Thanks to @Peter Richie, I found that GetJsonAsync<dynamic> does work if I explicitly cast MyProperty to a string. But I'd rather not have to do that. Using my original method and a real working endpoint, here are 3 scenarios; only the last one works:

var url = "http://echo.jsontest.com/MyProperty/MyValue"; // great testing site!

var x1 = await GetJsonAsync<dynamic>(url);
Assert.AreEqual("MyValue", x1.MyProperty); // fail!

dynamic x2 = await GetJsonAsync<dynamic>(url);
Assert.AreEqual("MyValue", x2.MyProperty); // fail!

dynamic x3 = await GetJsonAsync<ExpandoObject>(url);
Assert.AreEqual("MyValue", x3.MyProperty); // pass!

Armed with that knowledge, the non-generic overload of my original method looks like this:

public async Task<dynamic> GetJsonAsync(string url) {
    dynamic d = await GetJsonAsync<ExpandoObject>(url);
    return d;
}

And users can do this:

var x = await GetJsonAsync(url);
Assert.AreEqual("MyValue", x.MyProperty); // pass!