概要
JSON (JavaScript Object Notation) は、データを格納・交換するための軽量なテキストベースのフォーマットである。
C#では、JSONデータの操作が便利に行えるようになっている。
主に、System.Text.Json名前空間を使用してJSONの処理を行う。
この名前空間には、JSONのシリアライズ (オブジェクトをJSON文字列に変換) とデシリアライズ (JSON文字列をオブジェクトに変換) を行うためのクラスやメソッドが含まれている。
JSONファイルの読み込みは、通常のテキストファイルと同様にFile.ReadAllTextメソッドを使用して行う。
その後、JsonSerializer.Deserialize<T>メソッドを使用して、JSONデータをC#のオブジェクトに変換する。
逆に、C#のオブジェクトをJSONファイルとして保存する場合は、まず、JsonSerializer.Serializeメソッドを使用してオブジェクトをJSON文字列に変換して、
その後、File.WriteAllTextメソッドでファイルに書き込む。
JSONデータの操作には、動的な方法と静的な方法がある。
- 動的な方法
JsonDocument.Parseメソッドを使用してJSONデータを解析して、JsonElement型を通じてデータにアクセスする。
- 静的な方法
- JSONデータ構造に対応するC#のクラスを定義して、そのクラスのインスタンスとしてデータを扱う。
C#では、LINQ to JSONを使用することにより、JSONデータに対して複雑なクエリやフィルタリングを行うことも可能である。
これにより、大規模なJSONデータセットから必要な情報を効率的に抽出することができる。
※注意
大規模なJSONファイルを1度にメモリに読み込むとパフォーマンスの問題が生じる可能性がある。
このような場合は、JsonDocument.ParseAsyncメソッドを使用してストリーミング処理を行うことにより、メモリ使用量を抑えることができる。
また、セキュリティの観点から、信頼できないソースからのJSONデータを扱う場合は、適切なバリデーションを行うことが重要である。
不正なデータによる脆弱性を防ぐため、デシリアライズ時には型チェックや範囲チェックを実施することを推奨する。
動的な方法 : JsonDocument.Parseメソッドの使用
JSONファイルの読み込み
以下の例では、非同期処理およびストリーミング処理を使用して、JSONファイルの読み込んでいる。
具体的には、JsonDocument.ParseAsyncメソッドを使用してJSONデータを非同期で解析して、JsonElementクラスを通じてデータにアクセスしている。
また、FileStreamクラスを使用してストリーミング処理することにより、大きなJSONファイルを扱う場合にもメモリ効率が向上している。
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
class JsonFileReader
{
public static async Task ReadJsonFileAsync(string filePath)
{
try
{
using FileStream fileStream = File.OpenRead(filePath);
using JsonDocument document = await JsonDocument.ParseAsync(fileStream);
JsonElement root = document.RootElement;
// JSONの構造に応じて、適切にデータにアクセス
if (root.TryGetProperty("name", out JsonElement nameElement))
{
Console.WriteLine($"名前: {nameElement.GetString()}");
}
if (root.TryGetProperty("age", out JsonElement ageElement))
{
Console.WriteLine($"年齢: {ageElement.GetInt32()}");
}
if (root.TryGetProperty("hobbies", out JsonElement hobbiesElement) && hobbiesElement.ValueKind == JsonValueKind.Array)
{
Console.WriteLine("趣味:");
foreach (JsonElement hobby in hobbiesElement.EnumerateArray())
{
Console.WriteLine($"- {hobby.GetString()}");
}
}
}
catch (FileNotFoundException)
{
Console.WriteLine("エラー: 指定されたファイルが存在しない");
}
catch (JsonException ex)
{
Console.WriteLine($"JSONパースエラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
}
}
以下の例では、上記のクラスを使用してJSONファイルを読み込んでいる。
class Program
{
static async Task Main(string[] args)
{
string readFilePath = "sample.json";
// JSONファイルの読み込み
await JsonFileReader.ReadJsonFileAsync(readFilePath);
}
}
JSONファイルの書き込み
以下の例では、非同期処理およびストリーミング処理を使用して、JSONファイルを書き込んでいる。
具体的には、JsonSerializer.SerializeAsyncメソッドを使用して、オブジェクトをJSONに非同期でシリアライズして、ファイルに書き込んでいる。
また、FileStreamクラスを使用してストリーミング処理することにより、大きなJSONファイルを扱う場合にもメモリ効率が向上している。
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
class JsonFileWriter
{
public static async Task WriteJsonFileAsync(string filePath, object data)
{
try
{
using FileStream createStream = File.Create(filePath);
await JsonSerializer.SerializeAsync(createStream, data, new JsonSerializerOptions
{
WriteIndented = true // 整形されたJSONを出力
});
Console.WriteLine("JSONファイルの書き込みが完了");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("エラー: ファイルへの書き込み権限がない");
}
catch (JsonException ex)
{
Console.WriteLine($"JSONシリアライズエラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
}
}
以下の例では、上記のクラスを使用してJSONファイルを書き込んでいる。
class Program
{
static async Task Main(string[] args)
{
string writeFilePath = "sample.json";
// JSONファイルの書き込み
var data = new
{
name = "山田太郎",
age = 30,
hobbies = new[] { "読書", "旅行", "料理" }
};
await JsonFileWriter.WriteJsonFileAsync(writeFilePath, data);
}
}
静的な方法 : JSONデータ構造に対応するC#のクラスの定義
JSONデータ構造に対応するクラス
using System.Text.Json.Serialization;
public class Person
{
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("age")]
public int Age { get; set; }
[JsonPropertyName("hobbies")]
public List<string> Hobbies { get; set; }
public Person()
{
Hobbies = new List<string>();
}
}
静的な方法を使用することにより、型安全性が向上して、IDEの補完機能を活用できる等のメリットがある。
また、JSONデータの構造が変更された場合でも、Personクラスを修正するだけで対応できるため、保守性も高くなる。
JSONファイルの読み込み
以下の例では、非同期処理およびストリーミング処理を使用して、JSONファイルを読み込んでいる。
具体的には、JsonSerializer.DeserializeAsync<Person>メソッドを使用して、JSONデータを非同期でPersonオブジェクトにデシリアライズしている。
また、FileStreamクラスを使用してストリーミング処理することにより、大きなJSONファイルを扱う場合にもメモリ効率が向上している。
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
public class StaticJsonFileReader
{
public static async Task<Person> ReadJsonFileAsync(string filePath)
{
try
{
using FileStream fileStream = File.OpenRead(filePath);
var options = new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
};
Person person = await JsonSerializer.DeserializeAsync<Person>(fileStream, options);
if (person != null)
{
Console.WriteLine($"名前: {person.Name}");
Console.WriteLine($"年齢: {person.Age}");
Console.WriteLine("趣味:");
foreach (var hobby in person.Hobbies)
{
Console.WriteLine($"- {hobby}");
}
}
return person;
}
catch (FileNotFoundException)
{
Console.WriteLine("エラー: 指定されたファイルが存在しない");
}
catch (JsonException ex)
{
Console.WriteLine($"JSONパースエラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
return null;
}
}
以下の例では、上記のクラスを使用してJSONファイルを読み込んでいる。
class Program
{
static async Task Main(string[] args)
{
string readFilePath = "sample.json";
// JSONファイルの読み込み
Person person = await StaticJsonFileReader.ReadJsonFileAsync(readFilePath);
}
}
JSONファイルの書き込み
以下の例では、非同期処理およびストリーミング処理を使用して、JSONファイルを書き込んでいる。
具体的には、JsonSerializer.SerializeAsyncメソッドを使用して、PersonオブジェクトをJSONに非同期でシリアライズして、ファイルに書き込んでいる。
また、FileStreamクラスを使用してストリーミング処理することにより、大きなJSONファイルを扱う場合にもメモリ効率が向上している。
using System;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
public class StaticJsonFileWriter
{
public static async Task WriteJsonFileAsync(string filePath, Person person)
{
try
{
using FileStream createStream = File.Create(filePath);
var options = new JsonSerializerOptions
{
WriteIndented = true // 整形されたJSONを出力
};
await JsonSerializer.SerializeAsync(createStream, person, options);
Console.WriteLine("JSONファイルの書き込みが完了");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("エラー: ファイルへの書き込み権限がない");
}
catch (JsonException ex)
{
Console.WriteLine($"JSONシリアライズエラー: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
}
}
以下の例では、上記のクラスを使用してJSONファイルを書き込んでいる。
class Program
{
static async Task Main(string[] args)
{
string writeFilePath = "sample.json";
// JSONファイルの読み込み
Person person = await StaticJsonFileReader.ReadJsonFileAsync(readFilePath);
if (person != null)
{
// 読み込んだデータを修正
person.Age++;
person.Hobbies.Add("プログラミング");
// 修正したデータをJSONファイルに書き込み
await StaticJsonFileWriter.WriteJsonFileAsync(writeFilePath, person);
}
}
}
サードパーティ製ライブラリ
C#には標準ライブラリの他にも、いくつかの人気の高いサードパーティ製JSONパーサーライブラリが存在する。
- Newtonsoft.Json (Json.NET)
- 長年に渡り最も広く使用されているJSONライブラリである。
- 機能が豊富で柔軟性が高く、多くのプロジェクトで採用されている。
- Newtonsoft.Jsonは機能が豊富で柔軟性が高いため、複雑なJSONデータ構造を扱う場合に適している。
- ServiceStack.Text
- 高速なJSONシリアライズ / デシリアライズを提供するライブラリであり、特にパフォーマンスを重視する場合に選択されることがある。
- ServiceStack.Textは、パフォーマンスを重視するアプリケーションで使用されることが多い。
- Jil
- 非常に高速なJSONシリアライゼーションライブラリとして知られている。
- Jilは、パフォーマンスを重視するアプリケーションで使用されることが多い。
- UTF8Json
- UTF-8バイナリデータとC#オブジェクト間の高速な変換を特徴とするライブラリである。
- UTF8Jsonは、大量のデータを高速に処理する必要がある場合に選択されることがある。
- Utf8Json
- UTF8Jsonと同様に、UTF-8エンコーディングに最適化された高速なJSONライブラリである。
- Utf8Jsonは、大量のデータを高速に処理する必要がある場合に選択されることがある。
Newtonsoft.Json (Json.NET) ライブラリ
Newtonsoft.Json (Json.NET) ライブラリとは
Newtonsoft.Json (Json.NET) は、C#およびその他の.NET言語で使用される非常に人気の高いJSONフレームワークである。
James Newton-King氏によって開発されており、長年にわたり.NETエコシステムにおいてデファクトスタンダードとしての地位を築いてきた。
このライブラリの主な特徴は、その柔軟性と豊富な機能セットにある。
JSON文字列とC#オブジェクト間の変換 (シリアライズ / デシリアライズ) を簡単に行うことができ、複雑なJSONデータ構造も容易に扱うことができる。
また、LINQ to JSONという機能を提供しており、これによりJSONデータに対して強力なクエリやマニピュレーションが可能になる。
Json.NETは高度にカスタマイズ可能であり、独自のコンバータを定義および既存の型に対するシリアライズ / デシリアライズの動作をカスタマイズ等をすることができる。
これにより、特殊なデータ形式や複雑なオブジェクトグラフも適切に処理することができる。
パフォーマンスの面でも、Json.NETは優れた結果を示している。
大規模なJSONデータセットを効率的に処理することができ、多くの実運用環境で使用されている。
Json.NETは、JSONスキーマの検証やJSONパスの実装等、JSONに関連する幅広い機能をサポートしている。
これらの機能により、JSONデータの操作や検証がより柔軟かつ強力に行えるようになっている。
Json.NETは長年にわたり広く使用されてきたため、豊富なドキュメントとコミュニティサポートが存在する。
そのため、問題があった場合でも解決策を見つけやすいというメリットがある。
ただし、最近の.NETでは、System.Text.JsonというMicrosoftの公式JSONライブラリが導入されたため、一部のシナリオではJson.NETの使用が減少傾向にある。
しかし、その豊富な機能セットと柔軟性から、多くのプロジェクトでJson.NETは依然として重要な役割を果たしている。
Json.NETのライセンスは、MITライセンスに準拠している。
LINQ to JSON : JSONファイルの読み込み
以下の例では、LINQ to JSON、非同期処理、ストリーミング処理を使用して、JSONファイルの読み込んでいる。
FileStreamクラスを使用してストリーミング処理することにより、大きなJSONファイルを扱う場合にもメモリ効率が向上している。
- 読み込みに使用するJSONファイル
{
"appDesc": {
"description": "SomeDescription",
"message": "SomeMessage"
},
"appName": {
"description": "Home",
"message": "Welcome",
"imp":["awesome","best","good"]
}
}
using System;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class JsonReader
{
public async Task ReadJsonFileAsync(string filePath)
{
try
{
using (var fileStream = File.OpenRead(filePath))
using (var streamReader = new StreamReader(fileStream))
using (var jsonReader = new JsonTextReader(streamReader))
{
var jsonObject = await JObject.LoadAsync(jsonReader);
// JSONデータの処理
Console.WriteLine("アプリの説明: " + jsonObject["appDesc"]["description"]);
Console.WriteLine("アプリの名前: " + jsonObject["appName"]["message"]);
// 配列の処理
var impArray = jsonObject["appName"]["imp"].ToObject<string[]>();
Console.WriteLine("重要な特徴:");
foreach (var item in impArray)
{
Console.WriteLine("- " + item);
}
}
}
catch (FileNotFoundException)
{
Console.WriteLine("エラー: 指定されたJSONファイルが存在しない");
}
catch (JsonException ex)
{
Console.WriteLine($"エラー: JSONの解析中にエラーが発生: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
}
}
class Program
{
static async Task Main(string[] args)
{
// JSONファイルの読み込み
var reader = new JsonReader();
await reader.ReadJsonFileAsync("sample.json");
}
}