2

So I'm using an HttpClient to retrieve an HttpResponseMessage, where I read the content out as a string:

using (var responseStream = response.Content.ReadAsStringAsync())
{
    var streamContent = responseStream.Result;
}

Now, streamContent seems to be a JSON formatted string, but what I want to get to is payload, which again seems to be a JSON formatted string. The JSON visualizer (VS) gives me this:

[JSON]
    [0]
        payload_bytes: 201
        payload: "{"type":"Models.Event.Partyhat","Id":"123"}"

I'm having a little trouble getting to payload let alone the JSON string residing inside it. I've tried fiddling around with JsonConvert, but that didn't help me much.

Any hints?

Update:

My attempt at deserializing:

var streamContent = JsonConvert.DeserializeObject<object>(responseStream.Result);

EDIT 2:

It might be worth adding the actual string:

[{"payload_bytes":201,"payload":"{\"type\":\"Models.Event.Partyhat\",\"Id\":\"123\"}"}]
10
  • Can you show your code for deserializing it Commented Jul 26, 2018 at 7:42
  • 1
    [JSON] [0] is no json i have ever seen, is that an exact representation of what is returned? Commented Jul 26, 2018 at 7:44
  • 2
    I don't understand the problem. Deserialize the response and then deserialize the payload field. Commented Jul 26, 2018 at 8:03
  • 1
    if you deserialise to object you 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 to dynamic instead. Commented Jul 26, 2018 at 8:05
  • 1
    @Khaine775 your code has a serious bug. Instead of accessing the stream in the using block , 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. use using (var responseStream = await response.Content.ReadAsStringAsync()). You don't need .Result after that. Commented Jul 26, 2018 at 8:10

2 Answers 2

3
string jsonText = ...;
dynamic root = JsonConvert.DeserializeObject(jsonText);        
string payloadText = root[0].payload;  // do not use 'var' here
dynamic payload = JsonConvert.DeserializeObject(payloadText);       

This gets you string typeText = payload.type; as "Models.Event.Partyhat"

Sign up to request clarification or add additional context in comments.

Comments

2

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 :

  1. There would be no need for this question
  2. .Value fails for simple types while implicit casting (ie serializing back to string) works
  3. 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();

9 Comments

Small nitpick: your payloadObject definitely is a string, that was the core of the question. And then payload is the (dynamic) object.
@bommelding it's definitely not a string. Try it. I copied the code directly from LINQPad after removing the calls to .Dump()
@bommelding your code works because it casts the JObject returned from payload to a string before assigning it to the variable
Yes, everything is a JObject if you look at the bits. But logically it must be a string, if that cast fails the input was invalid.
@bommelding you don't have to look at the bits, call .GetType(). The result is actually a JValue. That's not a string. Your code casts it to a string.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.