C Sharpの基礎 - JSON
概要
JSON (JavaScript Object Notation) は、データを格納・交換するための軽量なテキストベースのフォーマットである。
C#では、JSONデータの操作が便利に行えるようになっている。
C#の標準ライブラリでJSONを扱う場合は、System.Text.Json
名前空間を使用してJSONの処理を行う。
この名前空間には、JSONのシリアライズ (オブジェクトをJSON文字列に変換) とデシリアライズ (JSON文字列をオブジェクトに変換) を行うためのクラスやメソッドが含まれている。
System.Text.Json
名前空間は、.NET Core 3.0以降で導入されている。
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ライセンスに準拠している。
- Newtonsoft.Json (Json.NET) ライブラリの公式Webサイト
Newtonsoft.Json (Json.NET) ライブラリのインストール
RiderまたはVisual StudioからNuGetを使用して、Json.NETライブラリをインストールする。
- Riderの場合
- プロジェクトを開く。
- [ツール]メインメニュー - [Nuget] - [ソリューション の Nuget パッケージを管理] (または、[<プロジェクト名> の Nuget パッケージを管理])を選択する。
- メイン画面下部にある[パッケージ]タブから Json.NET と入力して検索する。
- メイン画面下部の右にある[+]ボタンを押下して、Json.NETライブラリをインストールする。
- Visual Studioの場合
- プロジェクトを開く。
- NuGetパッケージマネージャーを開く。
- [ツール]メインメニュー - [NuGetパッケージマネージャー]を選択して、[ソリューションのNuGetパッケージの管理]を選択する。
- または、ソリューションエクスプローラーでプロジェクトを右クリックして、コンテキストメニューから[NuGetパッケージの管理]を選択する。
- Json.NETライブラリを検索する。
- NuGetパッケージマネージャーの検索ボックスに Json.NET と入力して検索する。
- Json.NETライブラリのインストール
- 検索結果からJson.NETライブラリを選択して、[インストール]ボタンを押下する。
- インストールの確認ダイアログが表示されるので、[OK]ボタンを押下してインストールを完了する。
- 参照の確認
- インストールが完了した後、プロジェクトの参照にJson.NETライブラリが追加されていることを確認する。
- パッケージマネージャーコンソールからインストールする場合
- プロジェクトを開く。
- [表示]メインメニュー - [その他のウィンドウ] - [パッケージマネージャーコンソール]を選択して、パッケージマネージャーコンソールを開く。
- パッケージマネージャーコンソールから、Json.NETライブラリをダウンロードしてインストールする。
Install-Package Newtonsoft.Json
- ソリューションエクスプローラーのプロジェクトの参照において、Json.NETライブラリが追加されていることを確認する。
dotnet
コマンドを使用する場合- ターミナルを開く。
- プロジェクトのルートディレクトリに移動する。
- Json.NETライブラリをインストールする。
- 最新の安定版をインストールする場合
dotnet add package Newtonsoft.Json
- バージョンを指定してインストールする場合
dotnet add package Newtonsoft.Json --version <バージョン>
- ※注意
- プロジェクトがGit等のバージョン管理システムを使用している場合、これらの変更がトラッキングされることを確認すること。
- プロジェクトを再ビルドして、新しく追加されたパッケージが正しく統合されていることを確認することを推奨する。
プロジェクトにおいて、Json.NETライブラリを使用する場合は、ソースコードファイルの先頭にusingステートメントを追加する。
using Newtonsoft.Json;
using Newtonsoft.Json.Linq; // LINQ to JSONを使用する場合
また、大きなサイズなJSONファイルを扱う場合は、ストリーミング処理を使用することを推奨する。
LINQ to JSON
LINQ to JSONは、Newtonsoft.Json.Linq
名前空間に含まれるクラスとメソッドを使用してJSONデータを操作するための機能である。
LINQ to JSONを使用することにより、JSONデータの読み込み、操作、書き込みを簡単に行うことができる。
このアプローチは、動的なJSONデータの処理やJSONの構造が事前に完全には分からない場合に有効である。
また、LINQ to JSONは、C#標準LINQクエリ構文と組み合わせて使用することもできるため、より複雑なJSONデータの操作や検索を行うことができる。
また、C#標準のLINQクエリ構文を組み合わせて使用する場合、JSONの構造が複雑であっても、特定の条件に合う要素を柔軟に見つけることができる。
例えば、オブジェクトが複数存在する場合やネストされた構造の中に存在する場合でも、全ての該当箇所を編集することができる。
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}");
}
}
}
以下の例では、上記のクラスを使用してJSONファイルを読み込んでいる。
class Program
{
static async Task Main(string[] args)
{
// JSONファイルの読み込み
var reader = new JsonReader();
await reader.ReadJsonFileAsync("sample.json");
}
}
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 JsonWriter
{
public async Task WriteJsonFileAsync(string filePath, JObject jsonObject)
{
try
{
using (var fileStream = File.Create(filePath))
using (var streamWriter = new StreamWriter(fileStream))
using (var jsonWriter = new JsonTextWriter(streamWriter))
{
jsonWriter.Formatting = Formatting.Indented;
await jsonObject.WriteToAsync(jsonWriter);
}
Console.WriteLine("JSONファイルの書き込みが完了");
}
catch (IOException ex)
{
Console.WriteLine($"エラー: ファイルの書き込み中にエラーが発生: {ex.Message}");
}
catch (JsonException ex)
{
Console.WriteLine($"エラー: JSONの生成中にエラーが発生: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
}
}
以下の例では、上記のクラスを使用してJSONファイルを書き込んでいる。
class Program
{
static async Task Main(string[] args)
{
// JSONファイルの書き込み
var writer = new JsonWriter();
var jsonObject = new JObject
{
["appDesc"] = new JObject
{
["description"] = "SomeDescription",
["message"] = "SomeMessage"
},
["appName"] = new JObject
{
["description"] = "Home",
["message"] = "welcome",
["imp"] = new JArray { "awesome", "best", "good" }
}
};
await writer.WriteJsonFileAsync("sample.json", jsonObject);
}
}
LINQ to JSON : JSONファイルの変更
以下の例では、下記に示すJSONファイルの"appName"オブジェクトの"imp"配列の値を["hoge", "piyo"]に変更している。
- 変更前のJSONファイル
{
"appDesc": {
"description": "SomeDescription",
"message": "SomeMessage"
},
"appName": {
"description": "Home",
"message": "Welcome",
"imp":["awesome","best","good"]
}
}
- JsonModifierクラスのインスタンスを生成する。
- JSONファイルを読み込み、JObject.Parseメソッドを使用してJSONオブジェクトに変換する。
- JSONオブジェクトから、jsonObject["appName"]["imp"] = new JArray("hoge", "piyo");を使用して、"appName"オブジェクトの"imp"配列の値を["hoge", "piyo"]に変更する。
- 変更したJSONオブジェクトを新しいファイルに書き込む。
using System;
using System.IO;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class JsonModifier
{
public async Task ModifyAndWriteJsonFileAsync(string inputFilePath, string outputFilePath)
{
try
{
// JSONファイルを読み込む
string jsonContent = await File.ReadAllTextAsync(inputFilePath);
JObject jsonObject = JObject.Parse(jsonContent);
// appName.impの配列を変更 (LINQ to JSONを使用する場合)
jsonObject["appName"]["imp"] = new JArray("hoge", "piyo");
// appName.impの配列を変更 (通常のLINQを使用する場合)
//var appNameObjects = jsonObject.Descendants()
// .OfType<JProperty>()
// .Where(p => p.Name == "appName")
// .Select(p => p.Value as JObject);
//foreach (var appName in appNameObjects)
//{
// if (appName != null && appName["imp"] is JArray impArray)
// {
// // "imp"配列の内容を変更
// impArray.Replace(new JArray("hoge", "piyo"));
// }
//}
// 変更したJSONを新しいファイルに書き込む
await WriteJsonFileAsync(outputFilePath, jsonObject);
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"エラー: 入力ファイルが存在しない: {ex.Message}");
}
catch (JsonException ex)
{
Console.WriteLine($"エラー: JSONの解析中にエラーが発生: {ex.Message}");
}
catch (Exception ex)
{
Console.WriteLine($"予期せぬエラーが発生: {ex.Message}");
}
}
public async Task WriteJsonFileAsync(string filePath, JObject jsonObject)
{
try
{
using (var fileStream = File.Create(filePath))
using (var streamWriter = new StreamWriter(fileStream))
using (var jsonWriter = new JsonTextWriter(streamWriter))
{
jsonWriter.Formatting = Formatting.Indented;
await jsonObject.WriteToAsync(jsonWriter);
}
Console.WriteLine("JSONファイルの書き込みが完了");
}
catch (IOException ex)
{
Console.WriteLine($"エラー: ファイルの書き込み中にエラーが発生: {ex.Message}");
}
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 inputFilePath = "sample.json";
string outputFilePath = "newsample.json";
var jsonModifier = new JsonModifier();
await jsonModifier.ModifyAndWriteJsonFileAsync(inputFilePath, outputFilePath);
}
}