使用 C# 反序列化 JSON 数据
Deserialize JSON data with C#
我是 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;
服务器信息class:
public class ServerInfo
{
public string message { get; set; }
public string message_timestamp { get; set; }
}
在 C# 中,
reader.ToString()
会默认return的名字为class。在这种情况下,"System.IO.StreamReader"
你想要的是
reader.ReadToEnd()
这将 return 流的全部内容作为字符串。
这应该会使其起作用,但请注意,这不是最佳做法。在您了解有关 C# 的更多信息时需要考虑的几个方面:
- 正如 Aron 提到的,您应该将所有流和阅读器包装在 "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
中
- 正如 Fred 在他的代码中所演示的那样,您可以避免将流转换为字符串,而只需让 Json.Net 库执行此操作即可。
- 为确保正确转义和格式化请求 URL,您可以使用 UriBuilder class:
new UriBuilder("http", ip, port, path).Uri)
- 您可以使用较新的 async friendly HttpClient class 下载数据。
您的响应正文中的 json 是什么?
您可能想先进行一些测试,以确保响应正文可以正确反序列化到您的 ServerInfo class。这是个人喜好问题,但我喜欢更明确地做事,因为它有助于最大限度地减少未来的意外行为。
例如,您可以像这样装饰 ServerInfo class:
// 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 class 中,如下所示:
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 class 的显式 JSON 序列化属性。请注意,如果 属性 名称匹配,则它们不是绝对必要的。我这样做主要是为了向您展示如何在不需要实现自定义 JsonSerializer 的情况下获得对序列化行为的更多控制。
虽然 Jeff 说的是正确的,但为什么它不能正常工作。他的回答仍然不是 "fix" 您的代码的正确方法。字符串在 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;
}
我建议使用结构化 JSON 类型(对象和数组)访问 JSON 值的方法,而无需预定义要反序列化的类型(例如 ServerInfo 类型)。
JsonObject serverinfo = (JsonObject)JsonObject.Load(responseStream);
我是 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;
服务器信息class:
public class ServerInfo
{
public string message { get; set; }
public string message_timestamp { get; set; }
}
在 C# 中,
reader.ToString()
会默认return的名字为class。在这种情况下,"System.IO.StreamReader"
你想要的是
reader.ReadToEnd()
这将 return 流的全部内容作为字符串。
这应该会使其起作用,但请注意,这不是最佳做法。在您了解有关 C# 的更多信息时需要考虑的几个方面:
- 正如 Aron 提到的,您应该将所有流和阅读器包装在 "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 中
- 正如 Fred 在他的代码中所演示的那样,您可以避免将流转换为字符串,而只需让 Json.Net 库执行此操作即可。
- 为确保正确转义和格式化请求 URL,您可以使用 UriBuilder class:
new UriBuilder("http", ip, port, path).Uri)
- 您可以使用较新的 async friendly HttpClient class 下载数据。
您的响应正文中的 json 是什么?
您可能想先进行一些测试,以确保响应正文可以正确反序列化到您的 ServerInfo class。这是个人喜好问题,但我喜欢更明确地做事,因为它有助于最大限度地减少未来的意外行为。
例如,您可以像这样装饰 ServerInfo class:
// 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 class 中,如下所示:
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 class 的显式 JSON 序列化属性。请注意,如果 属性 名称匹配,则它们不是绝对必要的。我这样做主要是为了向您展示如何在不需要实现自定义 JsonSerializer 的情况下获得对序列化行为的更多控制。
虽然 Jeff 说的是正确的,但为什么它不能正常工作。他的回答仍然不是 "fix" 您的代码的正确方法。字符串在 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;
}
我建议使用结构化 JSON 类型(对象和数组)访问 JSON 值的方法,而无需预定义要反序列化的类型(例如 ServerInfo 类型)。
JsonObject serverinfo = (JsonObject)JsonObject.Load(responseStream);