使用C#反序列化JSON数据

时间:2015-04-04 17:02:07

标签: c# json json.net

我是C#的新用户,我的代码遇到了一些问题。 我不能反序列化JSON数据,我不明白为什么:

            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;

服务器信息类:

    public class ServerInfo
{
    public string message { get; set; }
    public string message_timestamp { get; set; }
}

4 个答案:

答案 0 :(得分:3)

在C#中,

reader.ToString()

默认返回类的名称。在这种情况下,&#34; System.IO.StreamReader&#34;

你想要的是

reader.ReadToEnd()

将以字符串形式返回流的全部内容。

这应该会使它发挥作用,但请注意,这不是最佳做法。在您了解有关C#的更多信息时需要考虑的几个方面:

  • 正如Aron所提到的,你应该在"using" statement中包装所有的流和读者,以利用Dispose pattern,这将让运行时知道它可以立即释放资源而不是waiting for the finalizer
  • 正如Fred在他的代码中演示的那样,你可以避免将流转换为字符串,只需让Json.Net库就可以了。
  • 为确保您正确转义并格式化请求网址,您可以使用UriBuilder课程:new UriBuilder("http", ip, port, path).Uri)
  • 您可以使用较新的async友好HttpClient课程来下载数据。

答案 1 :(得分:2)

尽管杰夫在为什么它无法正常工作时是正确的。他的回答仍然不是解决问题的正确方法。你的代码。字符串在C#中非常低效(就像几乎每种编程语言一样,我们尽可能地避免使用它们。)

所以你应该这样做。

        //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;
        }

答案 2 :(得分:0)

您的回复正文中出现的json是什么?

您可能希望从一个小测试开始,以确保响应正文可以正确反序列化到您的ServerInfo类。这是个人偏好的问题,但我喜欢更明确地做事,因为它有助于减少未来的意外行为。

例如,您可以像这样装饰您的ServerInfo类:

// 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; }
}

然后,您将完整的HttpWebResponse主体读入如下字符串:

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

最后,您将响应主体反序列化到ServerInfo类中,如下所示:

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

这假设您的json将采用以下格式(或类似结构):

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

当然,您应首先检查您的实际输入是否正确反序列化。我使用这个简单的代码段在单元测试中尝试了上面的格式:

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);

编辑:我完全赞同Aron,因为您不应该不必要地使用成员字段,并且始终确保正确处理流。

根据他的建议改进我的原始答案,我之前建议的代码如下:

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;

会改变这个,这会表现得更好并且不容易出错(我为了简洁而删除了评论,请参阅Aron对解释的回答):

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;
}

这仍然适用于我添加到ServerInfo类的显式JSON序列化属性。请注意,如果属性名称匹配,则不是必需的。我这样做主要是为了向您展示如何在不需要实现自定义JsonSerializer的情况下获得对序列化行为的更多控制。

答案 3 :(得分:0)

我建议使用结构化JSON类型(对象和数组)访问JSON值的方法,而不必预定义要反序列化的类型(例如ServerInfo类型)。

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