Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 536
Todd Menier Avatar asked Mar 26 '14 23:03

Todd Menier


People also ask

What does Jsonconvert Deserializeobject do?

Deserializes the JSON to the specified . NET type. Deserializes the JSON to the specified . NET type using a collection of JsonConverter.

What is JsonSerializer deserialize?

Serialization and deserialization in . NET application, JSON data format conversion to . NET objects and vice versa is very common. Serialization is the process of converting . NET objects such as strings into a JSON format and deserialization is the process of converting JSON data into . NET objects.

What is JsonSerializerSettings?

DefaultIgnoreCondition Property (System. Text. Json) Gets or sets a value that determines when properties with default values are ignored during serialization or deserialization.


2 Answers

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!
like image 147
Todd Menier Avatar answered Oct 13 '22 13:10

Todd Menier


It sounds like there's some information you haven't provided. The following works fine for me:

    private T ReadJson<T>(Stream stream)
    {
        using (var reader = new StreamReader(stream))
        {
            using (var jr = new JsonTextReader(reader))
            {
                dynamic d = new JsonSerializer().Deserialize(jr);
                return d;
            }
        }
    }
//...

var d = ReadJson<dynamic>(new MemoryStream(Encoding.UTF8.GetBytes("{'MyProperty' : 'MyValue'}")));
Debug.WriteLine((String)d.MyProperty);
like image 45
Peter Ritchie Avatar answered Oct 13 '22 13:10

Peter Ritchie