.NET 4.0 Taskパターンを使用して、http://api.usa.gov/jobs/search.json?query=nursing+jobs
から返されたJSONをデシリアライズしようとしています。このJSONを返します( 'JSONデータの読み込み' @ 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
を調べると、ステータスはFaultedです。 InnerExceptionは「タイプProjectName.Jobsはコレクションではありません」です。
私は、JsonArray属性と配列としてのジョブ(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}
このサイトのJSONを.NET 4.0タスクパターンでどのように処理しますか? .NET 4.5のawait async
パターンに移る前に、これを機能させたいと思います。
ANSWER UPDATE:
BrumScouseの答えで.NET 4.5 async awaitパターンを使用した例を次に示します。
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
名前空間を取り込む必要があります。
注:.ReadAsString
には.Content
メソッドがありません。そのため、.ReadAsStringAsync
メソッドを使用しました。
モデルを手回しにする代わりに、Json2csharp.com Webサイトのようなものを使用してみてください。貼り付けJSON応答の例では、より充実したものを生成し、生成されたクラスを取得します。これにより、少なくともいくつかの可動部分が取り除かれ、csharpでJSONの形状が得られ、シリアライザーがより簡単になり、属性を追加する必要がなくなります。
動作させてから、クラス名を修正して、命名規則に準拠し、後で属性を追加します。
編集:Ok少しいじってから、結果をジョブのリストに正常にデシリアライズしました(Json2csharp.comを使用してクラスを作成しました)
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; }
}
そして、コードの編集:
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();
これは、収容オブジェクトを取り除くことができることを意味します。これはタスク関連の問題ではなく、逆シリアル化の問題であることに注意してください。
編集2:
JSONオブジェクトを取得してVisual Studioでクラスを生成する方法があります。選択したJSONをコピーし、[編集]> [特殊貼り付け]> [JSONをクラスとして貼り付け]をクリックします。ここではページ全体がこれに当てられています。
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;
戻り値のタイプはサーバーに依存します。時々、応答は実際にはJSON配列ですが、text/plainとして送信されます
リクエストにacceptヘッダーを設定すると、正しいタイプが取得されます。
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
次に、JSONリストまたは配列にシリアル化できます。 @svickからのコメントをありがとう。
Acceptヘッダーを構成せずに取得した例外は、System.Net.Http.UnsupportedMediaTypeExceptionでした。
次のコードはクリーンで動作するはずです(テストされていませんが、私の場合は動作します):
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 = response.Content.ReadAsAsync<List<Job>>();