BEACHSIDE BLOG

Azure とか C# 好きなエンジニアの個人メモ ( ・ㅂ・)و ̑̑

C# で System.Text.Json 使って Json を操作するときに気になったポイント( .NET Core 3 ~)

C# では .NET Core 3系にて Json のシリアライザーとして System.Text.Json が生まれました。
長らくお世話になった Json.NET - Newtonsoft とのお別れです。

多少お作法が異なるので、個人的に気になった点を整理してみました。

TL;DR

  • シリアライズする Unicode の範囲がデフォルトでは狭いので、日本語とか使うなら必要に応じて設定する
  • JsonSerializerOptions で色々設定できるのでドキュメントをチェック
  • 派生クラスの取り扱い注意

まとめると、とりあえずドキュメントは読みましょうって話です。

基本的な使い方

準備

NuGet で System.Text.Json をインストールすれば利用できます。

シリアライズとデシリアライズ

単純な利用方法としては、こんな感じです。

基本的に Json.NET と同じ空気感で利用できます。(空気感とは...)

日本語を使うならエンコードの設定が必要

上記のコードで名前に日本語で「横浜」入れてみると、エンコードされません。

f:id:beachside:20200204152458p:plain:w600

\u6A2A\u6D5C と表示されてしまいます。文字コードのままですね。

これは、シリアライズする Unicode の範囲がデフォルトでは狭いからです。

下のコードの 6-10行目 にあるように UnicodeRanges を指定してあげれば日本語も問題なく利用できます。Serializer の各種オプションの指定には、JsonSerializerOptions というクラスを使います。これも Json.NET と似たような感じですね。

UnicodeRanges についてもう少し触れると、8行目でエンコーダーを All に指定しました。Unicode のコードの範囲を細かく指定することが可能で、その範囲はこちらに乗ってます。これ、細かく指定すべきなのかめんどいという意味で悩ましいです。

ついでに 9行目でなんとなくシリアライズする際にインデントがつくように設定してみました。

このコードを実行すると、以下図のようにインデント付きで日本語も正しく表示されます。

f:id:beachside:20200204153238p:plain:w600

JsonSerializerOptions

まずはプロパティ名をキャメルケースにするのはほぼ使うことでしょう。想像だけで書けそうなやつですね。

var options = new JsonSerializerOptions()
{
      PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};

前述でエンコーダーを指定したりインデントをつけたりと使った JsonSerializerOptions ですが、以下のリンクに記載の JsonSerializerOptions のプロパティで何が設定できるか確認できます。

個人的には、

  • IgnoreNullValues: シリアライズ時に値が Null の場合はプロパティを無視する (Default: false)
  • IgnoreReadOnlyProperties: Rシリアライズ時にeadonly のプロパティを無視する (Default: false)

とかはよく使うので意識してるところです。
ってか Json.NET では Reaonly property の無視って自分で Resolver 書いてた記憶があるけど...設定できたのかな...まぁいいや。

派生クラスでの動作

こんなクラス(以下コード)があったとしましょう。そして似たのがたくさんあると毎回 Serialize のコード書くのめんどいし、それ己の振る舞いでしょってケースであれば、親クラス作って ToJson メソッドを作って Serialize するようにしますよね。
Json.NET 時代はこれで派生したクラス、つまり Person クラスのプロパティが Serialize できましたが、System.Text.Json だと親クラスのプロパティしか取れません。

以下図のように親のプロパティである Id しか出力されません。

f:id:beachside:20200204155245p:plain:w600

これに関しては、以下でデフォルトではできないと記載がありました。あっそーかー残念って感想です。

ドキュメントに書いてある通り Serialize メソッドの Generics で object を指定してあげればいいって話ではあるのですが。ちょっぴり気持ち悪さを感じる気がします...。

    public class AnimalBase
    {
        public string Id { get; set; }

        public string ToJson() => JsonSerializer.Serialize<object>(this);
    }

参考

他にもドキュメントはとりあえず読んでおくべきところですが、その中でもいくつかピックアップしておきます。