First of all, the code has a serious bug. Instead of accessing the stream in the using block , it accesses the task that will eventually return a ... string. Then it blocks the call with .Result.
The code should look like this :
using (var json= await response.Content.ReadAsStringAsync())
{
var jsonArray=JsonConvert.DeserializeObject<dynamic>(json);
var payloadObject=jsonArray[0].payload;
var payload=JsonConvert.DeserializeObject<dynamic>(payloadObject.Value);
}
If the calling method isn't asynchronous, it should be turned into one.
By deserializing to dynamic you can use the indexer and .payload getter directly. The second line uses .Value to extract the payload text so it can be deserialized as well.
You can deserialize from the response stream directly instead of reading all the contents as a string. This is usefull when handling large responses since you avoid caching the entire response as a string before deserializing it.
var serializer = new JsonSerializer();
using (var stream= await response.Content.ReadAsStreamAsync())
using (var sr = new StreamReader(stream))
using (var jsonTextReader = new JsonTextReader(sr))
{
var jsonArray=JsonConvert.DeserializeObject<dynamic>(json);
var payloadObject=jsonArray[0].payload;
var payload=JsonConvert.DeserializeObject<dynamic>(payloadObject.Value);
}
A small warning
Using .Value will work only if the value is actually a string, but it will be very cheap. The source code for JValue.Value shows that it returns everything as an object without any conversion.
Casting to string will also work only for strings and simple types :
string payloadObject=jsonArray[0].payload;
This will go through a couple of levels of indirection before returning the actual value.
The source code shows that casting actually ends up calling Convert.ToString(v.Value, CultureInfo.InvariantCulture) which is actually a call to ToString(CultureInfo.InvariantCulture)
In the end, it will return the same object as .Value;
If the payload isn't a string then :
- There would be no need for this question
- .Value fails for simple types while implicit casting (ie serializing back to string) works
- Both .Value and implicit casting fail for objects, eg
{\"type\":\"Models.Event.Partyhat\",\"Id\":\"123\"}
So be careful of the JSON payload, no matter which method you use.
If you can't be certain, you can convert the payload to a string no matter what :
string payloadString=jsonArray[0].payload.ToString();
Or
var payloadObject=jsonArray[0].payload.ToString();
[JSON] [0]is no json i have ever seen, is that an exact representation of what is returned?objectyou won't have access to anything which came from the JSON. This is because deserialising is the act of mapping property names in the JSON string to property names in the given type. Object doens't have any matching properties so nothing will be deserialised. Either create a specific class (or hierarchy of classes) to represent your data structure, or you could do a quick-and-dirty job by deserialising todynamicinstead.usingblock , you access the task that will eventually return a stream. Then you block the call with.Result. Don't do that. Your current code will never close the stream. useusing (var responseStream = await response.Content.ReadAsStringAsync()). You don't need.Resultafter that.