使用.NET 4.0任务模式的 Deserialize JSON 数组或列表

方法对从 http://api.usa.gov/jobs/search.json?query=nursing+jobs返回的 JSON 进行反序列化。NET 4.0任务模式。它返回这个 JSON (‘ Load JSON data’@http://jsonviewer.stack.hu/)。

[
{
"id": "usajobs:353400300",
"position_title": "Nurse",
"organization_name": "Indian Health Service",
"rate_interval_code": "PA",
"minimum": 42492,
"maximum": 61171,
"start_date": "2013-10-01",
"end_date": "2014-09-30",
"locations": [
"Gallup, NM"
],
"url": "https://www.usajobs.gov/GetJob/ViewDetails/353400300"
},
{
"id": "usajobs:359509200",
"position_title": "Nurse",
"organization_name": "Indian Health Service",
"rate_interval_code": "PA",
"minimum": 42913,
"maximum": 61775,
"start_date": "2014-01-16",
"end_date": "2014-12-31",
"locations": [
"Gallup, NM"
],
"url": "https://www.usajobs.gov/GetJob/ViewDetails/359509200"
},
...
]

索引行动:

  public class HomeController : Controller
{
public ActionResult Index()
{
Jobs model = null;
var client = new HttpClient();
var task = client.GetAsync("http://api.usa.gov/jobs/search.json?query=nursing+jobs")
.ContinueWith((taskwithresponse) =>
{
var response = taskwithresponse.Result;
var jsonTask = response.Content.ReadAsAsync<Jobs>();
jsonTask.Wait();
model = jsonTask.Result;
});
task.Wait();
...
}

工作和职业类别:

  [JsonArray]
public class Jobs { public List<Job> JSON; }


public class Job
{
[JsonProperty("organization_name")]
public string Organization { get; set; }
[JsonProperty("position_title")]
public string Title { get; set; }
}

当我在 jsonTask.Wait();上设置断点并检查 jsonTask时,状态为 错误。 InnerException 是“键入 ProjectName。 Jobs 不是集合”

我从没有 JsonArray 属性和 Jobs 作为数组(Job [])的 Jobs 类型开始,得到了这个错误。

  public class Jobs { public Job[] JSON; }


+       InnerException  {"Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'ProjectName.Models.Jobs' because the type requires a JSON object (e.g. {\"name\":\"value\"}) to deserialize correctly.\r\n
To fix this error either change the JSON to a JSON object (e.g. {\"name\":\"value\"}) or change the deserialized type to an array or a type that implements a collection interface
(e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.\r\n
Path '', line 1, position 1."}  System.Exception {Newtonsoft.Json.JsonSerializationException}

如何使用。NET 4.0任务模式?我想得到这个工作之前,移动到 await async模式。NET 4.5.

答案更新:

下面是一个使用.NET 4.5异步等待模式和 brumScouse 回答的例子。

 public async Task<ActionResult>Index()
{
List<Job> model = null;
var client = newHttpClient();


// .NET 4.5 async await pattern
var task = await client.GetAsync(http://api.usa.gov/jobs/search.json?query=nursing+jobs);
var jsonString = await task.Content.ReadAsStringAsync();
model = JsonConvert.DeserializeObject<List<Job>>(jsonString);
returnView(model);
}

您将需要引入 System.Threading.Tasks名称空间。
注意: .Content上没有可用的 .ReadAsString方法,这就是为什么我使用 .ReadAsStringAsync方法。

218781 次浏览

Instead of handcranking your models try using something like the Json2csharp.com website. Paste In an example JSON response, the fuller the better and then pull in the resultant generated classes. This, at least, takes away some moving parts, will get you the shape of the JSON in csharp giving the serialiser an easier time and you shouldnt have to add attributes.

Just get it working and then make amendments to your class names, to conform to your naming conventions, and add in attributes later.

EDIT: Ok after a little messing around I have successfully deserialised the result into a List of Job (I used Json2csharp.com to create the class for me)

public class Job
{
public string id { get; set; }
public string position_title { get; set; }
public string organization_name { get; set; }
public string rate_interval_code { get; set; }
public int minimum { get; set; }
public int maximum { get; set; }
public string start_date { get; set; }
public string end_date { get; set; }
public List<string> locations { get; set; }
public string url { get; set; }
}

And an edit to your code:

        List<Job> model = null;
var client = new HttpClient();
var task = client.GetAsync("http://api.usa.gov/jobs/search.json?query=nursing+jobs")
.ContinueWith((taskwithresponse) =>
{
var response = taskwithresponse.Result;
var jsonString = response.Content.ReadAsStringAsync();
jsonString.Wait();
model = JsonConvert.DeserializeObject<List<Job>>(jsonString.Result);


});
task.Wait();

This means you can get rid of your containing object. Its worth noting that this isn't a Task related issue but rather a deserialisation issue.

EDIT 2:

There is a way to take a JSON object and generate classes in Visual Studio. Simply copy the JSON of choice and then Edit> Paste Special > Paste JSON as Classes. A whole page is devoted to this here:

http://blog.codeinside.eu/2014/09/08/Visual-Studio-2013-Paste-Special-JSON-And-Xml/

var response = taskwithresponse.Result;
var jsonString = response.ReadAsAsync<List<Job>>().Result;

The return type depends on the server, sometimes the response is indeed a JSON array but sent as text/plain

Setting the accept headers in the request should get the correct type:

client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

which can then be serialized to a JSON list or array. Thanks for the comment from @svick which made me curious that it should work.

The Exception I got without configuring the accept headers was System.Net.Http.UnsupportedMediaTypeException.

Following code is cleaner and should work (untested, but works in my case):

    var client = new HttpClient();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var response = await client.GetAsync("http://api.usa.gov/jobs/search.json?query=nursing+jobs");
var model = await response.Content.ReadAsAsync<List<Job>>();