以下のようなコレクション(配列)を表現した JSON 文字列を C# のオブジェクトにデシリアライズするときにハマった話を書きます。
こんなことにハマるのは無知だからだと言われそうですが、また無駄な時間を費やさないように備忘録として書いておくことにしました。
[
{
"id":1,
"text":"project #1",
"start_date":"2020-05-06",
"end_date":"2020-05-26",
"user":3,
"duration":20,
"parent":0,
"progress":0
},
{
"id":2,
"text":"Task #1",
"start_date":"2020-05-06",
"end_date":"2020-05-16",
"user":0,
"duration":10,
"parent":1,
"progress":0
},
{
"id":3,
"text":"Task #2",
"start_date":"2020-05-16",
"end_date":"2020-05-26",
"user":0,
"duration":10,
"parent":1,
"progress":0
}
]
先の記事「JSON 文字列から C# のクラス定義生成」で書きましたように、Visual Studio を使って JSON 文字列から C# のオブジェクトのクラス定義を生成することができます。
上の JSON 文字列から Visual Studio で C# のクラス定義を生成すると以下の通りとなります。
public class Rootobject
{
public Class1[] Property1 { get; set; }
}
public class Class1
{
public int id { get; set; }
public string text { get; set; }
public string start_date { get; set; }
public string end_date { get; set; }
public int user { get; set; }
public int duration { get; set; }
public int parent { get; set; }
public int progress { get; set; }
}
Newtonsoft.Json と JavaScriptSerializer で上に書いた JSON 文字列を Rootobject にデシリアライズしてみます。コード例は以下の通りです。
// Newtonsoft.Json
var result1 = JsonConvert.DeserializeObject<Rootobject>(jsonText);
// JavaScriptSerializer
var serializer = new JavaScriptSerializer();
var result2 = serializer.Deserialize<Rootobject>(jsonText);
そうすると、Newtonsoft.Json では JsonSerializationException 例外が、JavaScriptSerializer では InvalidOperationException 例外がスローされます。Newtonsoft.Json が表示するエラーメッセージは以下の通りでした。
Newtonsoft.Json.JsonSerializationException: 'Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'ConsoleAppJson.Rootobject' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
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.
Path '', line 1, position 1.'
これによると Rootobject ではダメで、List<Class1> にデシリアライズしろということのようです。やってみたら確かに List<Class1> にはデシリアライズできました。
なお、上のような配列だけの JSON 文字列ではなくて、例えば { "data": [ { ... },{ ... },{ ... } ] } のような形の JSON 文字列にした場合は、Visual Studio により生成される C# のクラス定義はほぼ同じになりますが、Rootobject にデシリアライズできます。
[ { ... },{ ... },{ ... } ] という形の JSON 文字列だけが要注意のようです。
.NET Framework 版の ASP.NET MVC は JavaScriptSerializer を、ASP.NET Web API は Newtonsoft.Json を使っているので、上のような JSON 文字列を送信してアクションメソッドに渡す場合が自分的に要注意だと思いました。