次の方法で共有


Newtonsoft.Json から System.Text.Json に移行する

この記事では、Newtonsoft.Json から System.Text.Json に移行する方法を示します。

System.Text.Json 名前空間は、JavaScript Object Notation (JSON) との間でのシリアル化と逆シリアル化の機能を提供します。 System.Text.Json ライブラリは、.NET Core 3.1 以降のバージョン用のランタイムに含まれています。 その他のターゲット フレームワークの場合は、System.Text.Json NuGet パッケージをインストールします。 このパッケージで以下がサポートされます。

  • .NET Standard 2.0 以降のバージョン
  • .NET Framework 4.6.2 およびそれ以降のバージョン
  • .NET Core 2.0、2.1、および 2.2

ヒント

AI 支援を使用して、GitHub Copilot で Newtonsoft.Json から移行できます

System.Text.Json は、主にパフォーマンス、セキュリティ、標準への準拠に焦点を当てています。 これには既定の動作について重要な違いがいくつかあり、Newtonsoft.Json との機能パリティを持つことがこの目的ではありません。 一部のシナリオでは、現在 System.Text.Json に組み込み機能がなくても、推奨される回避策があります。 それ以外のシナリオでは、回避策は実用的ではありません。

System.Text.Json チームは、最も頻繁に要求される機能の追加に注力しています。 お使いのアプリケーションで、欠如している機能を利用する必要がある場合は、シナリオのサポートを追加できるかどうかを確認するために、dotnet/runtime GitHub リポジトリでイシューを提出することを検討してください。

この記事のほとんどの内容は、JsonSerializer API の使用方法に関するものですが、JsonDocument (ドキュメント オブジェクト モデル: DOM を表します)、Utf8JsonReaderUtf8JsonWriter の型の使用方法に関するガイダンスも含まれています。

Visual Basic では Utf8JsonReader を使用できません。すなわち、カスタム コンバーターも記述できません。 ここで提示する回避策の多くでは、カスタム コンバーターを記述する必要があります。 C# でカスタム コンバーターを記述し、それを Visual Basic プロジェクトに登録できます。 詳細については、「Visual Basic のサポート」をご覧ください。

相違点のテーブル

次の表は、Newtonsoft.Json の機能と、それと同等の System.Text.Json の機能を一覧にしたものです。 同等機能は次のカテゴリに分けられます。

  • ✔️ 組み込み機能によってサポートされています。 System.Text.Json で同様の動作を得るには、属性またはグローバル オプションの使用が必要になる場合があります。
  • ⚠ サポートされていませんが、回避策を適用できます。 回避策はカスタム コンバーターですが、Newtonsoft.Json の機能と完全に等価ではない可能性があります。 これらの一部については、サンプル コードが例として提供されます。 Newtonsoft.Json のこれらの機能を利用する必要がある場合、移行するには、.NET オブジェクト モデルに対する変更やその他のコード変更が必要になります。
  • ❌ サポートされていません。回避策は実用的でないか不可能です。 Newtonsoft.Json のこれらの機能を利用する必要がある場合、移行を可能にするには大幅な変更が必要です。
Newtonsoft.Json の機能 System.Text.Json での同等機能
既定での大文字と小文字の区別のない逆シリアル化 ✔️ PropertyNameCaseInsensitive グローバル設定
キャメルケースのプロパティ名 ✔️ PropertyNamingPolicy グローバル設定
スネークケースのプロパティ名 ✔️ スネーク ケースの名前付けポリシー
最小限の文字のエスケープ ✔️ 厳密な文字エスケープ、構成可能
NullValueHandling.Ignore グローバル設定 ✔️ DefaultIgnoreCondition グローバル オプション
コメントを許可する ✔️ ReadCommentHandling グローバル設定
末尾のコンマを許可する ✔️ AllowTrailingCommas グローバル設定
カスタム コンバーターの登録 ✔️ 優先順位の順序が異なる
既定の最大深度は 64、構成可能 ✔️ 既定の最大深度は 64、構成可能
PreserveReferencesHandling グローバル設定 ✔️ ReferenceHandling グローバル設定
引用符で囲まれた数値をシリアル化または逆シリアル化する ✔️ NumberHandling グローバル設定、[JsonNumberHandling] 属性
不変のクラスと構造体への逆シリアル化 ✔️ JsonConstructor、C# 9 のレコード
フィールドのサポート ✔️ IncludeFields グローバル設定、[JsonInclude] 属性
DefaultValueHandling グローバル設定 ✔️ DefaultIgnoreCondition グローバル設定
[JsonProperty] での NullValueHandling の設定 ✔️ JsonIgnore 属性
[JsonProperty] での DefaultValueHandling の設定 ✔️ JsonIgnore 属性
文字列以外のキーを含む Dictionary を逆シリアル化する ✔️ Supported
非パブリック プロパティのセッターとゲッターのサポート ✔️ JsonInclude 属性
[JsonConstructor] 属性 ✔️ [JsonConstructor] 属性
ReferenceLoopHandling グローバル設定 ✔️ ReferenceHandling グローバル設定
コールバック ✔️ コールバック
NaN、Infinity、-Infinity ✔️ Supported
[JsonProperty] 属性での Required の設定 ✔️ [JsonRequired] 属性と C# 必須修飾子
プロパティを無視するための DefaultContractResolver ✔️ DefaultJsonTypeInfoResolver クラス
ポリモーフィックなシリアル化 ✔️ [JsonDerivedType] 属性
ポリモーフィックな逆シリアル化 ✔️ [JsonDerivedType] 属性の型識別子
文字列列挙型の値を逆シリアル化する ✔️ 文字列列挙型の値を逆シリアル化する
MissingMemberHandling グローバル設定 ✔️ 欠落しているメンバーを処理する
セッターを持たないプロパティを設定する ✔️ セッターを持たないプロパティを設定する
ObjectCreationHandling グローバル設定 ✔️ プロパティを置き換えるのではなく再利用する
さまざまな型のサポート 一部の型にはカスタム コンバーターが必要
推論された型を object のプロパティに逆シリアル化する 非サポート、回避策、サンプル
JSON の null リテラルを null 非許容値の型に逆シリアル化する 非サポート、回避策、サンプル
DateTimeZoneHandlingDateFormatString の設定 非サポート、回避策、サンプル
JsonConvert.PopulateObject メソッド 非サポート、回避策
System.Runtime.Serialization 属性のサポート 非サポート、回避策、サンプル
JsonObjectAttribute 非サポート、回避策
引用符なしのプロパティ名を許可する デザインでサポートされていません
文字列値を囲む単一引用符を許可する デザインでサポートされていません
文字列のプロパティに対して文字列ではない JSON 値を許可する デザインでサポートされていません
TypeNameHandling.All グローバル設定 デザインでサポートされていません
JsonPath クエリのサポート サポートされていません
構成可能な制限 サポートされていません

これは Newtonsoft.Json 機能の包括的なリストではありません。 この一覧には、GitHub の問題または StackOverflow の投稿でリクエストされたシナリオの多くが含まれています。 ここに一覧表示されたシナリオのうち、現在サンプル コードがないシナリオの 1 つに回避策を実装する場合、およびご自分の解決策を共有する場合は、このページの下部にある [フィードバック] セクションで [このページ] を選択してください。 これにより、問題がこのドキュメントの GitHub リポジトリに作成され、このページの [フィードバック] セクションにも記載されます。

既定の動作においての相違点

既定では System.Text.Json は厳格であり、確定的な動作を重視して、呼び出し元のために推測や解釈は行われません。 このライブラリは、パフォーマンスとセキュリティを確保するために意図的にこのように設計されています。 Newtonsoft.Json は既定で柔軟性を持っています。 設計上のこの基本的な違いが、既定の動作における次のような固有の相違点の多くで、その背景にあります。

大文字と小文字の区別のない逆シリアル化

逆シリアル化中、Newtonsoft.Json では、大文字と小文字の区別のないプロパティ名の照合が既定で行われます。 System.Text.Json の既定では大文字と小文字が区別されます。完全一致が行われるため、これによってパフォーマンスが向上します。 大文字と小文字の区別のない照合の実行方法については、「大文字と小文字を区別しないプロパティ照合」を参照してください。

ASP.NET Core を使用して System.Text.Json を間接的に使用している場合、Newtonsoft.Json のような動作を得るために何かをする必要はありません。 ASP.NET Core では、System.Text.Json を使用するときに、Camel 形式のプロパティ名および大文字と小文字を区別しない照合のための設定が指定されます。

ASP.NET Core を使用すると、引用符で囲まれた数値を既定で逆シリアル化することもできます。

最小限の文字のエスケープ

シリアル化中、Newtonsoft.Json では文字をエスケープしないままにすることが比較的許容されています。 つまり、xxxx が文字のコード ポイントである場合、その文字が \uxxxx には置き換えられません。 文字がエスケープされる場合、その文字の前に \ が出力されます (たとえば、"\" になります)。 System.Text.Json の場合、既定でより多くの文字がエスケープされ、クロスサイト スクリプティング (XSS) や情報漏えいの攻撃に対する多層防御保護が実現します。そのために 6 文字のシーケンスが使用されます。 System.Text.Json では、既定で ASCII 以外の文字がすべてエスケープされるため、Newtonsoft.JsonStringEscapeHandling.EscapeNonAscii を使用している場合は、何もする必要はありません。 また、System.Text.Json では、既定で HTML に影響する文字もエスケープされます。 System.Text.Json の既定の動作をオーバーライドする方法については、「System.Text.Json」を参照してください。

コメント

逆シリアル化中、Newtonsoft.Json では既定で JSON 内のコメントは無視されます。 System.Text.Json の既定では、コメントに対して例外がスローされます。System.Text.Json 仕様にそれが含まれていないためです。 コメントを許可する方法については、「コメントと末尾のコンマを許可する」を参照してください。

末尾のコンマ

逆シリアル化中、Newtonsoft.Json では既定で末尾のコンマは無視されます。 また、複数の末尾のコンマ (たとえば、[{"Color":"Red"},{"Color":"Green"},,]) も無視されます。 System.Text.Json の既定では、末尾のコンマに対して例外がスローされます。System.Text.Json 仕様でそれが許可されていないためです。 System.Text.Json で受け入れられるようにする方法については、「System.Text.Json」を参照してください。 複数の末尾のコンマが許可されるようにする方法はありません。

コンバーターの登録の優先順位

Newtonsoft.Json でのカスタム コンバーターの登録の優先順位は次のとおりです。

  • プロパティの属性
  • 型の属性
  • Converters コレクション

この順序は、Converters コレクション内のカスタム コンバーターが、型レベルの属性を適用して登録されたコンバーターによってオーバーライドされることを意味します。 これらの登録はどちらも、プロパティ レベルの属性によってオーバーライドされます。

System.Text.Json でのカスタム コンバーターの登録の優先順位は異なります。

  • プロパティの属性
  • Converters コレクション
  • 型の属性

ここでの違いは、Converters コレクション内のカスタム コンバーターによって型レベルの属性がオーバーライドされる点です。 この優先順位の背後にある意図は、実行時の変更によって設計時の選択がオーバーライドされるようにすることです。 優先順位を変更する方法はありません。

カスタム コンバーターの登録の詳細については、「カスタム コンバーターを登録する」を参照してください。

最大の深さ

Newtonsoft.Json の最新バージョンでは、既定で最大深度制限は 64 です。 System.Text.Json の既定の制限も 64 であり、JsonSerializerOptions.MaxDepth を設定して構成できます。

ASP.NET Core を使用し、System.Text.Json を間接的に使用している場合、深さ上限の既定値は 32 です。 この既定値はモデル バインドの場合と同じであり、JsonOptions クラスで設定されます。

JSON 文字列 (プロパティ名と文字列値)

逆シリアル化中、Newtonsoft.Json では、二重引用符か単一引用符で囲まれた、または引用符なしのプロパティ名が受け取入れられます。 文字列値は、二重引用符または単一引用符で囲まれたものが受け入れられます。 たとえば、Newtonsoft.Json では次の JSON が受け入れられます。

{
  "name1": "value",
  'name2': "value",
  name3: 'value'
}

System.Text.Json では、二重引用符で囲まれたプロパティ名と文字列値のみが受け入れられます。この形式が System.Text.Json 仕様で要求されており、有効な JSON と見なされる唯一の形式であるためです。

値を単一引用符で囲むと、次のメッセージと共に JsonException が発生します。

''' is an invalid start of a value.

文字列プロパティに対する文字列以外の値

Newtonsoft.Json では、文字列型のプロパティへの逆シリアル化の場合、数値やリテラル truefalse など、文字列以外の値が受け入れられます。 Newtonsoft.Json で次のクラスへの逆シリアル化が正常に行われてる JSON の例を、以下に示します。

{
  "String1": 1,
  "String2": true,
  "String3": false
}
public class ExampleClass
{
    public string String1 { get; set; }
    public string String2 { get; set; }
    public string String3 { get; set; }
}

System.Text.Json では、文字列以外の値は文字列プロパティに逆シリアル化されません。 文字列フィールドに対して文字列以外の値を受け取ると、次のメッセージと共に JsonException が発生します。

The JSON value could not be converted to System.String.

JsonSerializer を使用するシナリオ

次のシナリオの一部は、組み込み機能ではサポートされていませんが、回避策を適用できます。 回避策とはカスタム コンバーターのことですが、これによって Newtonsoft.Json の機能との完全なパリティが得られるとは限りません。 これらの一部については、サンプル コードが例として提供されます。 Newtonsoft.Json のこれらの機能を利用する必要がある場合、移行するには、.NET オブジェクト モデルに対する変更やその他のコード変更が必要になります。

次のシナリオの一部では、回避策は実用的でないか不可能です。 Newtonsoft.Json のこれらの機能を利用する必要がある場合、移行を可能にするには大幅な変更が必要です。

引用符で囲まれた数値を許可または記述する

Newtonsoft.Json では、JSON 文字列によって表された (引用符で囲まれた) 数値をシリアル化または逆シリアル化できます。 たとえば、{"DegreesCelsius":23} ではなく {"DegreesCelsius":"23"} を受け入れることができます。 System.Text.Json でその動作を有効にするには、JsonSerializerOptions.NumberHandlingWriteAsString または AllowReadingFromString に設定するか、System.Text.Json 属性を使用します。

ASP.NET Core を使用して System.Text.Json を間接的に使用している場合、Newtonsoft.Json のような動作を得るために何かをする必要はありません。 System.Text.Json が使用されていて、Web の既定値で引用符で囲まれた数値が許可されている場合、ASP.NET Core により Web の既定値が指定されます。

詳細については、「引用符で囲まれた数値を許可または記述する」を参照してください。

逆シリアル化のときに使用するコンストラクターを指定する

Newtonsoft.Json[JsonConstructor] 属性を使用すると、POCO への逆シリアル化時に呼び出すコンストラクターを指定できます。

System.Text.Json には、System.Text.Json 属性もあります。 詳細については、「不変の型とレコード」を参照してください。

プロパティを条件付きで無視する

Newtonsoft.Json には、シリアル化または逆シリアル化時にプロパティを条件付きで無視する方法がいくつか用意されています。

  • DefaultContractResolver を使用すると、任意の条件に基づいて含めたり無視したりするプロパティを選択できます。
  • JsonSerializerSettingsNullValueHandlingDefaultValueHandling の設定を使用すると、null 値または既定値のすべてのプロパティを無視することを指定できます。
  • [JsonProperty] 属性の NullValueHandlingDefaultValueHandling の設定を使用すると、null または既定値に設定されている場合に無視する個々のプロパティを指定できます。

System.Text.Json には、シリアル化中にプロパティまたはフィールドを無視するために次の方法が用意されています。

さらに、.NET 7 以降のバージョンでは、任意の条件に基づいてプロパティを無視するように JSON コントラクトをカスタマイズできます。 詳細については、「カスタム コントラクト」を参照してください。

パブリックおよび非パブリック フィールド

Newtonsoft.Json では、フィールドおよびプロパティをシリアル化および逆シリアル化できます。

System.Text.Json では、シリアル化または逆シリアル化のときにフィールドを含めるには、JsonSerializerOptions.IncludeFields グローバル設定または System.Text.Json 属性を使用します。 例については、「フィールドを含める」を参照してください。

オブジェクト参照を保持してループを処理する

既定では、Newtonsoft.Json では値によってシリアル化します。 たとえば、オブジェクトに同じ Person オブジェクトへの参照を含む 2 つのプロパティが含まれている場合、その Person オブジェクトのプロパティの値は JSON 内で複製されます。

Newtonsoft.Json には、JsonSerializerSettings に、参照によってシリアル化できるようにする PreserveReferencesHandling 設定があります。

  • 最初の Person オブジェクト用に作成された JSON に識別子メタデータが追加されます。
  • 2番目の Person オブジェクト用に作成された JSON には、プロパティ値ではなく、その識別子への参照が含まれます。

Newtonsoft.Json には、例外をスローするのではなく循環参照を無視できるようにする ReferenceLoopHandling 設定もあります。

System.Text.Json で参照を保持し、循環参照を処理するには、JsonSerializerOptions.ReferenceHandlerPreserve に設定します。 ReferenceHandler.Preserve 設定は、Newtonsoft.Json での PreserveReferencesHandling = PreserveReferencesHandling.All に相当します。

ReferenceHandler.IgnoreCycles オプションは、Newtonsoft.JsonReferenceLoopHandling.Ignore と同様に動作します。 1 つの違いは、System.Text.Json の実装では、参照ループが、オブジェクト参照を無視するのではなく、null JSON トークンに置き換えられている点です。 詳細については、「循環参照を無視する」を参照してください。

Newtonsoft.JsonNewtonsoft.Json のように、シリアル化と逆シリアル化で参照を維持する動作が、System.Text.Json.Serialization.ReferenceResolver クラスによって定義されます。 カスタム動作を指定するには、派生クラスを作成します。 例については、GuidReferenceResolver に関するページを参照してください。

一部の関連する Newtonsoft.Json 機能はサポートされていません:

詳細については、「参照を保持し、循環参照を処理する」を参照してください。

文字列以外のキーを含むディクショナリ

Newtonsoft.JsonSystem.Text.Json のどちらでも、Dictionary<TKey, TValue> 型のコレクションがサポートされています。 ただし、System.Text.Json では、TKey はカスタムの型ではなく、プリミティブ型にする必要があります。 詳細については、サポートされているキーの型に関するページをご覧ください。

注意事項

TKeystring 以外のものとして型指定される Dictionary<TKey, TValue> に逆シリアル化すると、それを使用するアプリケーションでセキュリティに脆弱性が生じるおそれがあります。 詳細については、「dotnet/runtime#4761」を参照してください。

組み込みサポートのない型

System.Text.Json には、次の型に対する組み込みサポートはありません。

カスタム コンバーターは、組み込みサポートのない型に対して実装できます。

ポリモーフィックなシリアル化

Newtonsoft.Json では、ポリモーフィックなシリアル化が自動的に行われます。 .NET 7 以降、System.Text.Json では、JsonDerivedTypeAttribute 属性を使用したポリモーフィックなシリアル化がサポートされています。 詳細については、「派生クラスのプロパティをシリアル化する」を参照してください。

ポリモーフィックな逆シリアル化

Newtonsoft.Json には、シリアル化中に型名のメタデータを JSON に追加する TypeNameHandling 設定があります。 これは、逆シリアル化中にそのメタデータを使用して、ポリモーフィックな逆シリアル化を行います。 .NET 7 以降では、ポリモーフィックな逆シリアル化を実行するために、System.Text.Json は型判別子情報に依存します。 このメタデータは JSON で出力され、逆シリアル化中に使用され、基本型と派生型のどちらに逆シリアル化するかを判断します。 詳細については、「派生クラスのプロパティをシリアル化する」を参照してください。

以前の .NET バージョンでポリモーフィックな逆シリアル化をサポートするには、「カスタム コンバーターを記述する方法」の例のようなコンバーターを作成します。

文字列列挙型の値を逆シリアル化する

既定では、文字列列挙型の値の逆シリアル化は System.Text.Json ではサポートされませんが、Newtonsoft.Json ではサポートされます。 たとえば、次のコードでは JsonException がスローされます。

string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";
var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.

class MyObj
{
    public string Text { get; set; } = "";
    public MyEnum Enum { get; set; }
}

enum MyEnum
{
    One,
    Two,
    Three
}

ただし、JsonStringEnumConverter コンバーターを使用して文字列列挙型の値の逆シリアル化を有効にすることができます。 詳細については、「文字列としての列挙型」を参照してください。

オブジェクト プロパティの逆シリアル化

Newtonsoft.Json では、Object への逆シリアル化中に以下が行われます。

  • JSON ペイロード内のプリミティブ値 (null 以外) の型を推測し、格納されている stringlongdoubleboolean、または DateTime をボックス化されたオブジェクトとして返します。 "プリミティブ値" は、JSON の数値、文字列、truefalsenull などの 1 つの JSON 値です。
  • JSON ペイロード内の複合値の JObject または JArray を返します。 "複合値" は、中かっこ ({}) 内の JSON のキーと値のペアのコレクション、または角かっこ ([]) 内の値のリストです。 中かっこまたは角かっこ内のプロパティと値には、追加のプロパティまたは値を含めることができます。
  • ペイロードに null JSON リテラルが含まれている場合は、null 参照を返します。

System.Text.Json では、Object への逆シリアル化中には常に、プリミティブと複合の両方の値に対してボックス化された JsonElement が格納されます。例えば、以下のようなものです。

  • object プロパティ。
  • object ディクショナリ値。
  • object 配列値。
  • ルート object

ただし、System.Text.Json では nullNewtonsoft.Json と同じ方法で処理され、ペイロードに null JSON リテラルが含まれている場合は null 参照が返されます。

object プロパティに型の推定を実装するには、objectに関する記事の例にあるようなコンバーターを作成します。

null を null 非許容型に逆シリアル化する

Newtonsoft.Json では、次のシナリオでは例外がスローされません。

  • NullValueHandlingIgnore に設定されている。さらに
  • 逆シリアル化中に、JSON の null 非許容値型に null 値が含まれている。

System.Text.Json では、同じシナリオで例外がスローされます。 (System.Text.Json での対応する null 処理設定は JsonSerializerOptions.IgnoreNullValues = true です。)

ターゲット型を所有している場合、最適な回避策は、問題のプロパティを null 許容にすることです (たとえば、intint? に変更します)。

もう 1 つの回避策として、DateTimeOffset 型に対する null 値を処理する次の例のように、型のコンバーターを作成する方法があります。

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
    public class DateTimeOffsetNullHandlingConverter : JsonConverter<DateTimeOffset>
    {
        public override DateTimeOffset Read(
            ref Utf8JsonReader reader,
            Type typeToConvert,
            JsonSerializerOptions options) =>
            reader.TokenType == JsonTokenType.Null
                ? default
                : reader.GetDateTimeOffset();

        public override void Write(
            Utf8JsonWriter writer,
            DateTimeOffset dateTimeValue,
            JsonSerializerOptions options) =>
            writer.WriteStringValue(dateTimeValue);
    }
}

このカスタム コンバーターを登録するには、プロパティで属性を使用するか、Converters コレクションにコンバーター を追加します。

注: 上記のコンバーターでは、既定値を指定する POCO に対して Newtonsoft.Json が処理する場合とは異なる方法で null 値が処理されます。 たとえば、次のコードがターゲット オブジェクトを表しているとします。

public class WeatherForecastWithDefault
{
    public WeatherForecastWithDefault()
    {
        Date = DateTimeOffset.Parse("2001-01-01");
        Summary = "No summary";
    }
    public DateTimeOffset Date { get; set; }
    public int TemperatureCelsius { get; set; }
    public string Summary { get; set; }
}

さらに、上記のコンバーターを使用して、次の JSON が逆シリアル化されるとします。

{
  "Date": null,
  "TemperatureCelsius": 25,
  "Summary": null
}

逆シリアル化が行われた後、Date プロパティには 1/1/0001 (default(DateTimeOffset)) が設定されます。つまり、コンストラクターで設定された値が上書きされます。 同じ POCO と JSON を使用する場合、Newtonsoft.Json の逆シリアル化では Date プロパティに 1/1/2001 が設定されたままになります。

不変のクラスと構造体への逆シリアル化

Newtonsoft.Json では、パラメーターを持つコンストラクターを使用できるため、不変のクラスと構造体に逆シリアル化できます。

System.Text.Json では、パラメーター化されたコンストラクターの使用を指定するには System.Text.Json 属性を使用します。 C# 9 のレコードも不変であり、逆シリアル化ターゲットとしてサポートされています。 詳細については、「不変の型とレコード」を参照してください。

必須プロパティ

Newtonsoft.Json では、[JsonProperty] 属性に Required を設定して、プロパティが必須であることを指定します。 必須とマーク付けされたプロパティに対して JSON で値が取得されない場合、Newtonsoft.Json で例外がスローされます。

.NET 7 以降では、必須プロパティで C# required 修飾子または JsonRequiredAttribute 属性を使用できます。 JSON ペイロードにマークされたプロパティの値が含まれていない場合、System.Text.Json では例外がスローされます。 詳細については、「必須プロパティ」を参照してください。

日付形式を指定する

Newtonsoft.Json には、DateTime および DateTimeOffset 型のプロパティをどのようにシリアル化および逆シリアル化するかを制御する方法がいくつか用意されています。

  • DateTimeZoneHandling 設定を使用すると、すべての DateTime 値を UTC 日付としてシリアル化できます。
  • DateFormatString 設定と DateTime コンバーターを使用すると、日付文字列の形式をカスタマイズできます。

System.Text.Json は、RFC 3339 プロファイルを含む ISO 8601-1:2019 をサポートしています。 この形式は広く採用されており、明確で、正確にラウンドトリップを行います。 他の形式を使用するには、カスタム コンバーターを作成します。 たとえば、次のコンバーターを使用すると、Unix エポックのタイム ゾーン オフセットがある形式とない形式 (/Date(1590863400000-0700)//Date(1590863400000)/ などの値) を使用する JSON がシリアル化および逆シリアル化されます。

sealed class UnixEpochDateTimeOffsetConverter : JsonConverter<DateTimeOffset>
{
    static readonly DateTimeOffset s_epoch = new(1970, 1, 1, 0, 0, 0, TimeSpan.Zero);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)([+-])(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

    public override DateTimeOffset Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime)
                || !int.TryParse(match.Groups[3].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int hours)
                || !int.TryParse(match.Groups[4].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out int minutes))
        {
            throw new JsonException();
        }

        int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;
        TimeSpan utcOffset = new(hours * sign, minutes * sign, 0);

        return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
    }

    public override void Write(Utf8JsonWriter writer, DateTimeOffset value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
        TimeSpan utcOffset = value.Offset;

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}{utcOffset:hhmm})/");

        writer.WriteStringValue(formatted);
    }
}
sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>
{
    static readonly DateTime s_epoch = new(1970, 1, 1, 0, 0, 0);
    static readonly Regex s_regex = new("^/Date\\(([+-]*\\d+)\\)/$", RegexOptions.CultureInvariant);

    public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        string formatted = reader.GetString()!;
        Match match = s_regex.Match(formatted);

        if (
                !match.Success
                || !long.TryParse(match.Groups[1].Value, System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out long unixTime))
        {
            throw new JsonException();
        }

        return s_epoch.AddMilliseconds(unixTime);
    }

    public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
    {
        long unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);

        string formatted = string.Create(CultureInfo.InvariantCulture, $"/Date({unixTime})/");
        writer.WriteStringValue(formatted);
    }
}

詳細については、「System.Text.Json での DateTime と DateTimeOffset のサポート」を参照してください。

コールバック

Newtonsoft.Json では、シリアル化または逆シリアル化プロセスの複数の時点でカスタム コードを実行できます。

  • OnDeserializing (オブジェクトの逆シリアル化の開始時点)
  • OnDeserialized (オブジェクトの逆シリアル化の完了時点)
  • OnSerializing (オブジェクトのシリアル化の開始時点)
  • OnSerialized (オブジェクトのシリアル化の完了時点)

System.Text.Json では、シリアル化のときも逆シリアル化のときも同じ通知を公開します。 それらを使用するには、System.Text.Json.Serialization 名前空間から次のインターフェイスを 1 つまたは複数実装します。

次に示すのは、シリアル化と逆シリアル化の開始と終了で null プロパティがないか確認し、メッセージを書き込む例です。

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Callbacks
{
    public class WeatherForecast : 
        IJsonOnDeserializing, IJsonOnDeserialized, 
        IJsonOnSerializing, IJsonOnSerialized
    {
        public DateTime Date { get; set; }
        public int TemperatureCelsius { get; set; }
        public string? Summary { get; set; }

        void IJsonOnDeserializing.OnDeserializing() => Console.WriteLine("\nBegin deserializing");
        void IJsonOnDeserialized.OnDeserialized()
        {
            Validate();
            Console.WriteLine("Finished deserializing");
        }
        void IJsonOnSerializing.OnSerializing()
        {
            Console.WriteLine("Begin serializing");
            Validate();
        }
        void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished serializing");

        private void Validate()
        {
            if (Summary is null)
            {
                Console.WriteLine("The 'Summary' property is 'null'.");
            }
        }
    }

    public class Program
    {
        public static void Main()
        {
            var weatherForecast = new WeatherForecast
            {
                Date = DateTime.Parse("2019-08-01"),
                TemperatureCelsius = 25,
            };

            string jsonString = JsonSerializer.Serialize(weatherForecast);
            Console.WriteLine(jsonString);

            weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString);
            Console.WriteLine($"Date={weatherForecast?.Date}");
            Console.WriteLine($"TemperatureCelsius={weatherForecast?.TemperatureCelsius}");
            Console.WriteLine($"Summary={weatherForecast?.Summary}");
        }
    }
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}

//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=

OnDeserializing コードでは、新しい POCO インスタンスにアクセスできません。 逆シリアル化の開始時に新しい POCO インスタンスを操作するには、そのコードを POCO コンストラクターに挿入します。

非パブリック プロパティのセッターとゲッター

Newtonsoft.Json では、JsonProperty 属性を通じて、プライベートおよび内部プロパティのセッターとゲッターを使用できます。

System.Text.Json では、System.Text.Json 属性を通じて、プライベートおよび内部プロパティのセッターとゲッターがサポートされます。 サンプル コードについては、「パブリックでないプロパティ アクセサー」を参照してください。

既存のオブジェクトの設定

Newtonsoft.JsonJsonConvert.PopulateObject メソッドでは、新しいインスタンスを作成するのではなく、クラスの既存のインスタンスに JSON ドキュメントが逆シリアル化されます。 System.Text.Json では常に、既定のパラメーターなしのパブリック コンストラクターを使用して、ターゲット型の新しいインスタンスが作成されます。 カスタム コンバーターによって、既存のインスタンスに逆シリアル化できます。

プロパティを置き換えるのではなく再利用する

.NET 8 以降では、System.Text.Json を使用すると、初期化されたプロパティを置き換える代わりに再利用できます。 動作にはいくつかの違いがあり、API の提案に関するページで確認できます。

詳細については、「初期化されたプロパティの設定」を参照してください。

セッターを持たないプロパティを設定する

.NET 8 以降では、System.Text.Json を使用すると、セッターを持たないプロパティを含むプロパティの設定を行うことができます。 詳細については、「初期化されたプロパティの設定」を参照してください。

スネーク ケースの名前付けポリシー

System.Text.Json には、スネーク ケースの組み込みの名前付けポリシーが含まれています。 ただし、一部の入力では Newtonsoft.Json との動作の違いがあります。 次の表は、JsonNamingPolicy.SnakeCaseLower ポリシーを使用して入力を変換するときのこれらの違いをいくつか示しています。

入力 Newtonsoft.Json の結果 System.Text.Json の結果
"AB1" "a_b1" "ab1"
"SHA512Managed" "sh_a512_managed" "sha512_managed"
"abc123DEF456" "abc123_de_f456" "abc123_def456"
"KEBAB-CASE" "keba_b-_case" "kebab-case"

System.Runtime.Serialization 属性

DataContractAttributeDataMemberAttribute などの System.Runtime.Serialization 属性、IgnoreDataMemberAttribute ではデータ コントラクトを定義できます。 データ コントラクトは、サービスとクライアントの間の正式な取り決めであり、交換されるデータが抽象的に記述されています。 データ コントラクトは、交換のためにシリアル化されるプロパティを正確に定義します。

System.Text.Json には、これらの属性の組み込みサポートがありません。 ただし、.NET 7 以降では、 カスタム型リゾルバー を使用してサポートを追加できます。 サンプルについては、「ZCS.DataContractResolver」を参照してください 。

8 進数

Newtonsoft.Json では、先頭にゼロを持つ数値は 8 進数として扱われます。 System.Text.Json では先頭のゼロは許容されません。System.Text.Json 仕様で許可されていないためです。

不足しているすべてのメンバーを処理します

逆シリアル化されている JSON にターゲット型に存在しないプロパティが含まれている場合は、Newtonsoft.Json は例外をスローするようにを構成できます。 System.Text.Json の既定では、[JsonExtensionData] 属性を使用する場合を除き、JSON 内の余分なプロパティは無視されます。

.NET 8 以降のバージョンでは、次のいずれかの方法を使用して、マップされていない JSON プロパティをスキップするか禁止するかを設定できます。

JsonObjectAttribute

Newtonsoft.Jsonには 属性 JsonObjectAttribute があります。この属性は、シリアル化されるメンバー、null 値の処理方法、およびすべてのメンバーが必要かどうかを制御するために、型レベル で適用できます。 System.Text.Json には、型に適用できる同等の属性がありません。 null 値の処理などの一部の動作では、グローバル JsonSerializerOptions で同じ動作を構成することも、各プロパティで個別に構成することもできます。

Newtonsoft.Json.JsonObjectAttribute を使用 してすべての null プロパティを無視するように指定する次の例を考えてみましょう:

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }

System.Text.Json では、 すべての型とプロパティの動作を設定できます:

JsonSerializerOptions options = new()
{
    DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

string json = JsonSerializer.Serialize<Person>(person, options);

または、 各プロパティの動作を個別に設定することもできます:

public class Person
{
    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public string? Name { get; set; }

    [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
    public int? Age { get; set; }
}

次に、Newtonsoft.Json.JsonObjectAttribute を使用して、すべてのメンバー プロパティが JSON に存在する必要があることを指定する次の例を考えてみましょう:

[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }

System.Text.Json では、C# required 修飾子または JsonRequiredAttribute を各プロパティに追加することで、同じ動作を実現できます。 詳細については、「必須プロパティ」を参照してください。

public class Person
{
    [JsonRequired]
    public string? Name { get; set; }

    public required int? Age { get; set; }
}

TraceWriter

Newtonsoft.Json では、TraceWriter を使用してシリアル化または逆シリアル化によって生成されたログを表示し、デバッグできます。 System.Text.Json では、ログ記録は行われません。

JToken (JObject、JArray など) と比較した JsonDocument と JsonElement

System.Text.Json.JsonDocument には、既存の JSON ペイロードからSystem.Text.Json.JsonDocumentのドキュメント オブジェクト モデル (DOM) を解析して作成する機能が用意されています。 DOM では、JSON ペイロード内のデータへのランダム アクセスが提供されます。 ペイロードを構成する JSON 要素には、JsonElement 型を使用してアクセスできます。 JsonElement 型には、JSON テキストを一般的な .NET 型に変換するための API が用意されています。 JsonDocument では RootElement プロパティが公開されます。

.NET 6 以降では、System.Text.Json.Nodes 名前空間の JsonNode 型とその他の型を使用して、既存の JSON ペイロードから変更可能な DOM を解析および構築できます。 詳細については、「JsonNode の使用」を参照してください。

JsonDocument は IDisposable

JsonDocument では、データのメモリ内ビューがプールされたバッファー内に作成されます。 そのため、Newtonsoft.JsonJObjectJArray とは異なり、JsonDocument 型は IDisposable を実装し、Using ブロック内で使用される必要があります。 詳細については、「JsonDocument は IDisposable」を参照してください。

JsonDocument は読み取り専用

System.Text.Json の DOM では、JSON 要素の追加、削除、変更を行うことはできません。 このように設計されている理由は、パフォーマンスを向上させ、一般的な JSON ペイロード サイズ (つまり、< 1 MB) を解析するための割り当てを減らすためです。

JsonElement は union 構造体

JsonDocument では、JsonElement 型 (任意の JSON 要素を含む union 構造体型) のプロパティとして RootElement が公開されます。 Newtonsoft.Json では JObjectJArrayJToken などの専用の階層型が使用されます。 JsonElement に対して検索と列挙を行うことができます。また、JsonElement を使用して、JSON 要素を .NET 型に具体化することができます。

.NET 6 以降では、JsonNode 型と、JObjectJArrayJToken に対応する System.Text.Json.Nodes 名前空間内の型を使用できます。 詳細については、「JsonNode の使用」を参照してください。

JsonDocument と JsonElement でのサブ要素の検索方法

Newtonsoft.JsonJObject または JArray を使用した JSON トークンの検索は、何らかのディクショナリ内での検索であるため、比較的高速になる傾向があります。 それに対し、JsonElement での検索にはプロパティの順次検索が必要になるため、比較的低速になります (たとえば、TryGetProperty を使用する場合)。 System.Text.Json は、検索時間ではなく、初期解析時間を最小限に抑えるように設計されています。 詳細については、「JsonDocument と JsonElement でのサブ要素の検索方法」を参照してください。

Utf8JsonReader と JsonTextReader

System.Text.Json.Utf8JsonReader は、UTF-8 でエンコードされた JSON テキスト用の、ハイ パフォーマンス、低割り当て、順方向専用のリーダーです。System.Text.Json.Utf8JsonReader または < から読み取られます。 Utf8JsonReader は低レベルの型であり、カスタム パーサーとデシリアライザーを構築するために使用できます。

Utf8JsonReader は ref 構造体

Newtonsoft.JsonJsonTextReader はクラスです。 Utf8JsonReader 型は "Utf8JsonReader" であるという点で異なります。 詳細については、「Utf8JsonReader の ref 構造体の制限事項」を参照してください。

null 許容値型に null 値を読み込む

Newtonsoft.Json には、Nullable<T> を返す API が用意されています。たとえば、bool? を返すことで Null TokenType を処理する ReadAsBoolean などがあります。 組み込みの System.Text.Json の API では、null 非許容値型のみが返されます。 詳細については、「null 許容値型に null 値を読み込む」を参照してください。

JSON を読み取るためのマルチターゲット

特定のターゲット フレームワークに対して Newtonsoft.Json の使用を継続する必要がある場合は、マルチターゲットにし、実装を 2 つにすることができます。 ただし、これは簡単ではなく、いくつかの #ifdefs とソースの複製が必要になります。 できるだけ多くのコードを共有する 1 つの方法として、Utf8JsonReaderNewtonsoft.Json.JsonTextReader を囲む ref struct ラッパーを作成します。 このラッパーでは、動作の違いを隔離しつつ、公開されている表面部分は統合されます。 これにより、変更を主に型の構造に隔離すると共に、新しい型を参照によって渡すことができます。 これは、Microsoft.Extensions.DependencyModel ライブラリが従っているパターンです。

Utf8JsonWriter と JsonTextWriter

System.Text.Json.Utf8JsonWriter は、StringInt32DateTime のような一般的な .NET 型から UTF-8 でエンコードされた JSON テキストを書き込むための、ハイパフォーマンスな方法です。 ライターは低レベルの型であり、カスタム シリアライザーを構築するために使用できます。

生の値を書き込む

Newtonsoft.Json には、値が必要な生の JSON を書き込む WriteRawValue メソッドがあります。 System.Text.Json には直接的に同等の Utf8JsonWriter.WriteRawValue があります。 詳細については、生 JSON の書き込みに関するページを参照してください。

JSON 形式をカスタマイズする

JsonTextWriter には次の設定が含まれていますが、Utf8JsonWriter には同等のものがありません。

  • QuoteChar - 文字列値を囲むために使用する文字を指定します。 Utf8JsonWriter では常に二重引用符が使用されます。
  • QuoteName - プロパティ名を引用符で囲むかどうかを指定します。 Utf8JsonWriter では常に引用符で囲みます。

.NET 9 以降では、JsonWriterOptions 構造体によって公開されるオプションを使用して、Utf8JsonWriter のインデント文字とサイズをカスタマイズできます。

JsonTextWriter には次の設定が含まれていますが、Utf8JsonWriter には同等のものがありません。

  • Indentation - インデントする文字数を指定します。 Utf8JsonWriter の場合は、常に 2 文字ずつインデントします。
  • IndentChar - インデントに使用する文字を指定します。 Utf8JsonWriter では常に空白文字が使用されます。
  • QuoteChar - 文字列値を囲むために使用する文字を指定します。 Utf8JsonWriter では常に二重引用符が使用されます。
  • QuoteName - プロパティ名を引用符で囲むかどうかを指定します。 Utf8JsonWriter では常に引用符で囲みます。

Utf8JsonWriter によって生成された JSON をこれらの方法でカスタマイズできる回避策はありません。

Timespan、Uri、または char の値を書き込む

JsonTextWriter には、JsonTextWriterWriteValuechar の値のための WriteValue メソッドがあります。 Utf8JsonWriter には同等のメソッドがありません。 代わりに、これらの値を文字列として書式設定し (たとえば ToString() を呼び出します)、WriteStringValue を呼び出します。

JSON を書き込むためのマルチターゲット

特定のターゲット フレームワークに対して Newtonsoft.Json の使用を継続する必要がある場合は、マルチターゲットにし、実装を 2 つにすることができます。 ただし、これは簡単ではなく、いくつかの #ifdefs とソースの複製が必要になります。 できるだけ多くのコードを共有する 1 つの方法として、Utf8JsonWriterNewtonsoft.Json.JsonTextWriter を囲むラッパーを作成します。 このラッパーでは、動作の違いを隔離しつつ、公開されている表面部分は統合されます。 これにより、主に型の構造に変更を隔離できます。 Microsoft.Extensions.DependencyModel ライブラリは以下に従っています。

TypeNameHandling.All はサポートされていません。

System.Text.Json から TypeNameHandling.All-equivalent 機能を除外するという決定は意図的なものでした。 独自の型情報を指定することを JSON ペイロードに許可することは、Web アプリケーションにおいて、よくある脆弱性の原因です。 特に、TypeNameHandling.AllNewtonsoft.Json を構成すると、JSON ペイロード自体の中に実行可能アプリケーション全体を埋め込むことがリモート クライアントに許可されます。逆シリアル化の場合、組み込まれたコードが Web アプリケーションによって抽出され、実行されます。 詳細については、「Friday the 13th JSON attacks PowerPoint」 (13 日の金曜日の JSON 攻撃)(PowerPoint 資料から) と「Friday the 13th JSON attacks details」 (13 日の金曜日の JSON 攻撃の詳細) を参照してください。

JSON パス クエリは非サポート

JsonDocument DOM では、JsonDocumentを使用したクエリはサポートされていません。

JsonNode DOM では、各 JsonNode インスタンスに、そのノードへのパスを返す GetPath メソッドが含まれます。 ただし、JSON パス クエリ文字列に基づくクエリを処理する組み込み API はありません。

詳細については、dotnet/runtime #31068 GitHub イシューを参照してください。

一部の制限は構成できません

System.Text.Json により、文字数での最大トークン サイズ (166 MB) や base 64 の場合 (125 MB) のように、一部の値に対して変更できない制限が設定されています。 詳細については、ソースコードの JsonConstants および GitHub のイシュー dotnet/runtime #39953 を参照してください。

NaN、Infinity、-Infinity

Newtonsoft によって、NaNInfinity-Infinity の JSON 文字列トークンが解析されます。 System.Text.Json では、JsonNumberHandling.AllowNamedFloatingPointLiterals を使用します。 この設定の使用方法の詳細については、「引用符で囲まれた数値を許可または記述する」を参照してください。

GitHub Copilot を使用して移行する

GitHub Copilot からコーディングのヘルプを取得して、IDE 内でコードを変換できます。

Visual Studio 2022 バージョン 17.8 以降を使用している場合は、Visual Studio で AI 駆動型の GitHub Copilot を試して Newtonsoft.Json コードを移行して System.Text.Json を使用できます。 次の例のように、プロンプトとして変換する質問とコードを Copilot のチャット ウィンドウに送信します。

Note

GitHub Copilot では AI を利用しているため、想定外のことや間違いが起こる可能性があります。 生成されたコードまたは提案を必ず確認してください。 GitHub Copilot の一般的な用途、製品への影響、人間の監視、プライバシーの詳細については、GitHub Copilot に関する FAQ を参照してください。

Copilot Chat のサンプル プロンプト

convert the following code to use System.Text.Json
Product product = new Product();

product.Name = "Apple";
product.ExpiryDate = new DateTime(2024, 08, 08);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };

string output = JsonConvert.SerializeObject(product);
Console.WriteLine(output);

Copilot Chat の応答の例

To convert the code to use System.Text.Json, you can use the JsonSerializer class. Here's the updated code:
using System;
using System.Text.Json;

public class Product
{
    public string Name { get; set; }
    public DateTime ExpiryDate { get; set; }
    public decimal Price { get; set; }
    public string[] Sizes { get; set; }
}

public class Program
{
    public static void Main()
    {
        Product product = new Product();

        product.Name = "Apple";
        product.ExpiryDate = new DateTime(2024, 08, 08);
        product.Price = 3.99M;
        product.Sizes = new string[] { "Small", "Medium", "Large" };

        string output = JsonSerializer.Serialize(product);
        Console.WriteLine(output);
    }
}
This code uses System.Text.Json.JsonSerializer.Serialize to convert the Product object to a JSON string.

結果は、これらの応答例に示されているものとは異なる場合があることに注意してください。 AI モデルは非決定論的です。つまり、同じ質問を受けたときに異なる応答を返す場合があります。 これは、時間の経過に伴う学習と適応の進行、言語バリエーション、チャット履歴などのコンテキストの変更などが原因である可能性があります。

Visual Studio で GitHub Copilot Chat を使用して newtonsoft から移行している様子を示した、アニメーション化されたスクリーンショット

スラッシュ コマンド、参照、スレッドなどのチャット機能を使用して、インテントを設定し、範囲を絞ったコンテキストでより適切な回答を得ることができます。 たとえば、コード ファイル filename が IDE で開かれている場合は、Copilot Chat が「#filename を変換して System.Text.Json を使用」するようにプロンプトでファイルを参照できます。 チャット ウィンドウまたはインライン チャットで「@workspace を変換して System.Text.Json を使用」を使用してソリューションを参照することもできます。

その他のリソース