はじめに
皆さん、JSON使ってますか?C#でJSONを使うならJson.NETが定番中の定番ですが、.NET Core 3.0以降からSystem.Text.Json
が標準で入るようになりました。一通り触ってみたので備忘録としてまとめておきます。
何が嬉しいの?
メリット
- サードパーティーライブラリに依存せずに済む
- 配布ファイルのサイズ削減
- UTF-16を介さずUTF-8文字列を直接読み書きできるので高速(らしい)1
- あとなんだろ……
デメリット
- Json.NETからの移行には一部コードの修正が必要
- Json.NETで出来ていたことが一部出来ない
- 厳密な分あまり融通が利かない(パフォーマンス重視?)
- (現時点では)マイナーなので参考にできるサンプルが少ない
くらいでしょうか?今のところパフォーマンスが重要になる場面以外ではあんまり魅力的じゃない気もしますが、これからきっと流行ると願って……願って……。
導入
.NET Core 3.0以降
何もせずとも最初から入ってます。
NET Standard 2.0 以降、.NET Framework 4.6.1以降2、.NET Core 2.x
nugetから落としましょう。
シリアライズ
まずは普通に
こんなクラスがあったとして、
public class Person { public string Name { get; set; } public int Age { get; set; } public override string ToString() => $"Name:{Name}, Age:{Age}"; }
Json.NETみたいにこうやると、
using System; using System.Text.Json; var customers = new Person[] { new Person() { Name = "山田", Age = 29 }, new Person() { Name = "鈴木", Age = 22 } }; string json = JsonSerializer.Serialize(customers); Console.WriteLine(json);
出力は、
[{"Name":"\u5C71\u7530","Age":29},{"Name":"\u9234\u6728","Age":22}]
あれ?となります。これはSystem.Text.Json
が既定で基本ラテン文字以外をエスケープする仕様になっているからです。3人間が読む必要がなければこれでも良いですが、エスケープしないようにするには以下のように書いてあげます。ついでにWriteIndented = true
としてインデントを付けてあげました。
using System; using System.Text.Encodings.Web; using System.Text.Json; // (中略) var options = new JsonSerializerOptions { // JavaScriptEncoder.Createでエンコードしない文字を指定するのも可 Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // 読みやすいようインデントを付ける WriteIndented = true }; string json = JsonSerializer.Serialize(customers, options); Console.WriteLine(json);
出力
[ { "Name": "山田", "Age": 29 }, { "Name": "鈴木", "Age": 22 } ]
UTF-8ストリームに直接書き込む
JSONを使う場合って、ファイル操作やネットワーク処理など何らかのStream
を読み書きすることが多いと思います。この場合は直接Stream
を渡してあげましょう。4
using System; using System.Text.Encodings.Web; using System.Text.Json; using System.IO; // (中略) var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = true }; // FileStreamじゃなくてもStreamならOK using var stream = new FileStream(fileName, FileMode.Create, FileAccess.Write); // optionsは省略可。必要に応じてCancellationTokenも渡す。 await JsonSerializer.SerializeAsync(stream, customers, options);
Utf8JsonWriter
を引数に取るJsonSerializer.Serialize
のオーバーロードもあるみたいですが割愛。
プロパティ名をcamelCaseにする
camelCaseにしたいだけなら、オプションでJsonNamingPolicy.CamelCase
を指定してあげれば良いです。
// (前略) var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; // (後略)
出力
[ { "name": "山田", "age": 29 }, { "name": "鈴木", "age": 22 } ]
任意のプロパティ名にする
任意のプロパティ名にする場合は、対象のクラスにJsonPropertyName
属性を付けてあげます。
ちなみにJsonNamingPolicy.CamelCase
と同時に指定した場合、JsonPropertyName
属性の方が優先されます。
using System.Text.Json.Serialization; public class Person { [JsonPropertyName("name")] public string Name { get; set; } [JsonPropertyName("age")] public int Age { get; set; } public override string ToString() => $"Name:{Name}, Age:{Age}"; }
デシリアライズ
stringからデシリアライズ
雰囲気はJson.NETと同じです。オプションも適宜指定してあげてください。
using System; using System.Text.Encodings.Web; using System.Text.Json; string json = "[{\"name\":\"山田\",\"age\":29},{\"name\":\"鈴木\",\"age\":22}]"; var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; // optionsは省略可 Person[] customers = JsonSerializer.Deserialize<Person[]>(json, options); foreach (var customer in customers) { Console.WriteLine(customer); }
出力
Name:山田, Age:29 Name:鈴木, Age:22
Streamからデシリアライズ
特に書くことないです。
using System; using System.Text.Encodings.Web; using System.Text.Json; using System.IO; var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; using var stream = new FileStream(fileName, FileMode.Open, FileAccess.Read); // optionsは省略可 Person[] customers = await JsonSerializer.DeserializeAsync<Person[]>(stream, options);
大文字小文字を区別しない
オプションにPropertyNameCaseInsensitive = true
を指定します。
var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, PropertyNameCaseInsensitive = true }; Person[] customers = JsonSerializer.Deserialize<Person[]>(json, options);
コメントや末尾のコンマを許可
RFC 8259ではコメントや末尾のコンマは許可されていないため、そういったJSONをデシリアライズしようとすると例外が発生します。これらを許可する場合は以下のようにオプションを指定します。
var options = new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // コメントを許可 ReadCommentHandling = JsonCommentHandling.Skip, // 末尾のコンマを許可 AllowTrailingCommas = true }; Person[] customers = JsonSerializer.Deserialize<Person[]>(json, options);
その他もろもろ
ドキュメント見て
まとめ
というわけでSystem.Text.Json
の使い方覚え書きでした。あまり凝ったことしなければJson.NETみたいな感覚でまあ普通に使えるかなという感触です。パフォーマンスも気になるところなので、気が向いたら測定してみようかなあと思います。