C# Tips | 文字列処理:JSON圧縮

C# C#
スポンサーリンク

はじめに 「JSON圧縮」は“意味は変えずに、できるだけ短くする”こと

ここでいう「JSON圧縮」は、
GZipなどのバイナリ圧縮ではなく、

「余計な改行・インデント・スペースを削って、JSON文字列を最小限の長さにする」

いわゆる JSONの“ミニファイ(minify)” の話です。

APIレスポンスを少しでも軽くしたい
ログやファイルに保存するサイズを減らしたい
「整形済みJSON」を、送信用にコンパクトに戻したい

といった場面で、JSON圧縮ユーティリティは地味に役に立ちます。

ポイントは、

  • JSONの「意味」は一切変えない
  • 変えるのは「見た目(空白・改行)」だけ

ということです。


基本方針:一度パースしてから「インデントなし」で書き出す

なぜ「改行削除」だけではダメなのか

初心者がやりがちなのは、

  • 改行を全部消す
  • タブやスペースを全部消す

といった「文字列置換ベース」の圧縮です。

これは危険です。

文字列の中に含まれるスペースや改行(例: "Hello World"
JSONの構造上必要なスペース(例: "key": "value" のコロン後のスペース)

など、「消していい空白」と「消してはいけない空白」を区別できないからです。

正しいやり方は、整形のときと同じで、

  1. JSON文字列を一度パースして“構造”として理解する
  2. インデントなし・余計な空白なしでシリアライズし直す

という二段構えです。


実務で使える基本実装(System.Text.Json版)

完成形のユーティリティ

using System;
using System.Text.Json;

public static class JsonCompressUtil
{
    public static string CompressJson(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return string.Empty;
        }

        try
        {
            using var doc = JsonDocument.Parse(json);

            var options = new JsonSerializerOptions
            {
                WriteIndented = false
            };

            string compressed = JsonSerializer.Serialize(doc.RootElement, options);

            return compressed;
        }
        catch (JsonException)
        {
            return json;
        }
    }
}
C#

この1メソッドで、「JSON圧縮ユーティリティ」としてほぼ実務投入できます。
何をしているか、順番にかみ砕いていきます。


重要ポイント1:JsonDocument.Parse で“構造として”読み直す

using var doc = JsonDocument.Parse(json);
C#

ここでやっているのは、

  • JSON文字列をパースして
  • メモリ上の「JSONドキュメント」として扱えるようにする

という処理です。

これにより、

  • 改行やインデントがどうなっていようが関係なく
  • JSONの「キー」「値」「配列」「オブジェクト」などの構造だけを取り出せる

状態になります。

もしここで JsonException が出たら、その文字列は「JSONとして不正」です。
今回のユーティリティでは、

catch (JsonException)
{
    return json;
}
C#

として、「圧縮はあきらめて元の文字列を返す」方針にしています。


重要ポイント2:WriteIndented = false で“最小限のJSON”にする

var options = new JsonSerializerOptions
{
    WriteIndented = false
};

string compressed = JsonSerializer.Serialize(doc.RootElement, options);
C#

JsonSerializer.Serialize は、
オブジェクトをJSON文字列に変換するメソッドです。

WriteIndented = false(デフォルト)を指定すると、

  • 改行なし
  • インデントなし
  • 必要最小限のスペースだけ

という「コンパクトなJSON」が出力されます。

つまり、

  • パース → JsonDocument
  • インデントなしでシリアライズ → 圧縮済みJSON文字列

という流れで、「意味はそのまま、見た目だけ最小化」しているわけです。


動作例で違いを確認する

整形済みJSONを圧縮する

string pretty = @"{
  ""id"": 1,
  ""name"": ""Taro"",
  ""tags"": [
    ""dev"",
    ""backend""
  ],
  ""active"": true
}";

string compressed = JsonCompressUtil.CompressJson(pretty);

Console.WriteLine(compressed);
C#

出力イメージは次のようになります。

{"id":1,"name":"Taro","tags":["dev","backend"],"active":true}

中身(キーや値、配列の要素)は一切変わっていませんが、
改行とインデントが消え、1行に詰まっています。

もともと1行JSONを渡した場合

string raw = "{\"id\":1,\"name\":\"Taro\"}";

string compressed = JsonCompressUtil.CompressJson(raw);

Console.WriteLine(compressed); // {"id":1,"name":"Taro"}
C#

この場合は、ほぼそのまま返ってきます。
「整形済みでも、1行でも、とりあえず“正規化されたコンパクトJSON”にする」
というイメージです。


不正JSONへの扱いをどうするか

「壊れていても、とりあえず見たい」か、「絶対に弾きたい」か

JsonDocument.Parse は厳密なので、
途中で切れているJSONや、余計な文字が混ざったJSONに対しては JsonException を投げます。

今回の実装では、

  • 正しいJSON → 圧縮して返す
  • 不正なJSON → 元の文字列をそのまま返す

という挙動にしています。

理由は、

  • ログやデバッグ用途では、「壊れていても中身を見たい」ことが多い
  • 圧縮ユーティリティが例外でアプリ全体を落とすのは避けたい

からです。

一方で、「外部からの入力を厳密に検証したい」ような場面では、
ここで例外をそのまま投げる実装に変えるのもアリです。


Newtonsoft.Json(Json.NET)版も押さえておく

既存プロジェクトでよく使われるパターン

Json.NETを使う場合も、考え方は同じです。

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public static class JsonCompressUtilNewtonsoft
{
    public static string CompressJson(string? json)
    {
        if (string.IsNullOrWhiteSpace(json))
        {
            return string.Empty;
        }

        try
        {
            var token = JToken.Parse(json);

            return token.ToString(Formatting.None);
        }
        catch (JsonReaderException)
        {
            return json;
        }
    }
}
C#

JToken.Parse でパースし、
ToString(Formatting.None) で「インデントなしJSON」を出力しています。

新規開発なら System.Text.Json を優先、
既存コードがJson.NETベースならこちら、という使い分けです。


業務ユーティリティとしてのまとめ方

「整形」と「圧縮」をセットで持っておくと強い

前に作った「JSON整形」と今回の「JSON圧縮」は、
ペアで持っておくとかなり便利です。

public static class JsonPretty
{
    public static string Format(string? json)
        => JsonFormatUtil.FormatJson(json);
}

public static class JsonMini
{
    public static string Compress(string? json)
        => JsonCompressUtil.CompressJson(json);
}
C#

これで、

  • デバッグ時 → JsonPretty.Format(responseBody)
  • 送信前・保存前 → JsonMini.Compress(json)

というように、用途に応じて簡単に切り替えられます。

nullや空文字の扱いをユーティリティ側で決めておく

今回の実装では、

  • null / 空 / 空白だけ → 空文字を返す
  • 不正JSON → 元の文字列を返す

というルールにしています。

これにより、呼び出し側は、

  • いちいちnullチェックを書かなくてよい
  • 例外処理を毎回書かなくてよい

というメリットがあります。

プロジェクトのポリシーに合わせて、

  • 不正JSONは例外
  • nullはそのままnull

などに変えるのもOKですが、
「ユーティリティ側で統一ルールを決める」のが大事です。


まとめ 「JSON圧縮ユーティリティ」は“通信や保存を少しだけ賢くする、小さな最適化”

JSON圧縮(ミニファイ)は、
見た目を犠牲にしてでも、サイズを少しでも減らしたいときに使うテクニックです。

押さえておきたいポイントは、

JSON圧縮は“改行削除”ではなく、“パースしてからインデントなしでシリアライズし直す”こと。
System.Text.Json なら JsonDocument.ParseJsonSerializer.Serialize(WriteIndented = false) で簡潔に書けること。
不正JSONへの扱い(例外にするか、元の文字列を返すか)をユーティリティ側で決めておくと、呼び出し側が楽になること。
「整形」と「圧縮」をセットで持っておくと、デバッグと本番の両方で使い分けやすいこと。

ここまで理解できれば、「なんとなく1行にしている」段階から一歩進んで、
“構造を意識した、安全で実務的なJSON圧縮ユーティリティ”を、
自分のC#コードの中に自然に組み込めるようになっていきます。

タイトルとURLをコピーしました