2

I am a new user in C# and I'm haveing some problems with my code. I cant deserialize JSON data and I cant understand why:

            webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........"); // Create a request to get server info
            webRequest.Method = "GET";
            webRequest.KeepAlive = true;
            webRequest.ContentType = "application/x-www-form-urlencoded";
            webRequest.CookieContainer = cookieJar; //set container for HttpWebRequest 

            webResponse = (HttpWebResponse)webRequest.GetResponse(); // Get the response.

            reader = new StreamReader(webResponse.GetResponseStream()); 
            ServerInfo outObject = JsonConvert.DeserializeObject<ServerInfo>(reader.ToString());
            my_label_ServerInfo.Text = outObject.message;

the server info class:

    public class ServerInfo
{
    public string message { get; set; }
    public string message_timestamp { get; set; }
}
Falanwe
  • 4,636
  • 22
  • 37
Gal Dalali
  • 99
  • 1
  • 2
  • 6
  • What's the error you're getting? – ZivS Apr 04 '15 at 17:10
  • For performance, you really shouldn't convert the `Stream` into a `string`. Try this instead. http://www.newtonsoft.com/json/help/html/DeserializeWithJsonSerializerFromFile.htm – Aron Apr 04 '15 at 18:37

4 Answers4

3

In C#,

reader.ToString()

will by default return the name of the class. In this case, "System.IO.StreamReader"

What you want is

reader.ReadToEnd()

which will return the entire contents of the stream as a string.

That should cause it to work, but be aware that it's not best practice. A few areas for consideration as you learn more about C#:

  • As Aron mentioned, you should wrap all your streams and readers in "using" statement to take advantage of the Dispose pattern which will let the runtime know it can release resources right away rather than waiting for the finalizer
  • As Fred demonstrated in his code, you can avoid converting the stream to a string and just let the Json.Net library do that.
  • To ensure that you properly escape and format the request URL, you could use the UriBuilder class: new UriBuilder("http", ip, port, path).Uri)
  • You could use the newer and async friendly HttpClient class to download the data.
Community
  • 1
  • 1
Jeff Moser
  • 19,727
  • 6
  • 65
  • 85
2

Although Jeff is correct in WHY it doesn't work correctly. His answer still isn't the correct way to "fix" your code. Strings are very inefficient in C# (like almost EVERY programming language, and we avoid them as much as possible).

So you should be doing this instead.

        //STOP USING member fields (when possible), 
        //you encourage threading problems with member fields.
        var webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........"); // Create a request to get server info
        webRequest.Method = "GET";
        webRequest.KeepAlive = true;
        webRequest.ContentType = "application/x-www-form-urlencoded";
        webRequest.CookieContainer = cookieJar; //set container for HttpWebRequest 

        var webResponse = (HttpWebResponse)webRequest.GetResponse(); 

        //ALWAYS dispose your disposable correctly
        //Not disposing HttpStreams will cause you to leak TCP/IP
        //ports.
        using(var stream = webResponse.GetResponseStream())
        using(var reader = new StreamReader(stream))
        {
            JsonSerializer serializer = new JsonSerializer();
            ServerInfo outObject = (ServerInfo)serializer.Deserialize(reader, typeof(ServerInfo));
            my_label_ServerInfo.Text = outObject.message;
        }
Aron
  • 15,464
  • 3
  • 31
  • 64
0

What is the json that comes in your response body?

You might want to start off with a little test to make sure that the response body can correctly deserialize into your ServerInfo class. This is a matter of personal preference, but I like to do things a bit more explicitly as it helps to minimize unexpected behavior down the road.

For example, you could decorate your ServerInfo class like this:

// I chose MemberSerialization.OptIn so that all members need to be
// included explicitly, rather than implicitly (which is the default)
[JsonObject(MemberSerialization.OptIn)]
public class ServerInfo
{
    [JsonProperty]
    public string message { get; set; }

    [JsonProperty]
    public string message_timestamp { get; set; }
}

Then, you read the full HttpWebResponse body into a string like this:

reader = new StreamReader(webResponse.GetResponseStream());
string responseBody = reader.ReadToEnd();
reader.Close();

And lastly, you deserialize the response body into your ServerInfo class like this:

ServerInfo serverInfo = JsonConvert.DeserializeObject<ServerInfo>(responseBody);

This is assuming your json will come in the following format (or of a similar structure):

{
    "message": "Test Message",
    "message_timestamp": "2015-04-04T20:00:00"
}

Of course you should first check if your actual input deserializes correctly. I tried the format above in a unit test with this simple snippet:

var sb = new StringBuilder();
sb.Append("{");
sb.AppendLine();

sb.AppendFormat("\"{0}\": \"{1}\"", "message", "Test Message");
sb.Append(",");
sb.AppendLine();

sb.AppendFormat("\"{0}\": \"{1}\"", "message_timestamp", "2015-04-04T20:00:00");
sb.AppendLine();

sb.Append("}");

string json = sb.ToString();

ServerInfo serverInfo = JsonConvert.DeserializeObject<ServerInfo>(json);

EDIT: I completely agree with Aron in that you shouldn't unnecessarily use member fields and always make sure to dispose streams properly.

Improving my original answer with his suggestions, the following code I suggested earlier:

webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........");
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookieJar;

webResponse = (HttpWebResponse)webRequest.GetResponse();

reader = new StreamReader(webResponse.GetResponseStream()); 
string responseBody = reader.ReadToEnd();
reader.Close();
ServerInfo serverInfo = JsonConvert.DeserializeObject<ServerInfo>
my_label_ServerInfo.Text = serverInfo.message;

Would change into this, which will perform better and is less prone to errors (I removed the comments for brevity, see Aron's answer for the explanations):

var webRequest = (HttpWebRequest)WebRequest.Create("http://" + Ip.ToString() + ":" + Port.ToString() + "........");
webRequest.Method = "GET";
webRequest.KeepAlive = true;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.CookieContainer = cookieJar;

var webResponse = (HttpWebResponse)webRequest.GetResponse();

using (var stream = webResponse.GetResponseStream())
using (var reader = new StreamReader(stream))
{
    JsonSerializer serializer = new JsonSerializer();
    ServerInfo serverInfo = (ServerInfo)serializer.Deserialize(reader, typeof(ServerInfo));
    my_label_ServerInfo.Text = serverInfo.message;
}

This will still work with the explicit JSON serialization attributes I added to your ServerInfo class. Note that they are not strictly necessary if the property names match up. I do this mainly just to show you how to gain more control over the serialization behavior without the need to implement a custom JsonSerializer.

Fred Kleuver
  • 7,797
  • 2
  • 27
  • 38
0

I'd suggest the approach of accessing JSON values with structured JSON types (object and array), without having to predefine types (such as the ServerInfo type) to deserialize into.

JsonObject serverinfo = (JsonObject)JsonObject.Load(responseStream);