チュートリアル

簡単にライブラリーを使用するためのチュートリアルコレクション

チュートリアル».NET»Spire.PDF for .NET»変換»C#で PDF を JSON に変換:テキスト、テーブル、フォーム対応
2026-07-13

C#で PDF を JSON に変換:テキスト、テーブル、フォーム対応

C#で PDF を JSON に変換 — テキスト、テーブル、フォームフィールドを構造化 JSON に抽出

アプリケーションが PDF の請求書を受信したとします。必要なものは請求書番号、取引先名、明細行です。しかし、それらはページ上のテキストとしてではなく、API が処理できる構造化された JSON として取得する必要があります。これが PDF から JSON への変換における本質的な課題です。

CSV や XML とは異なり、PDF ファイルには固有のデータ構造がありません。フィールドも、行も、スキーマも存在しません。利用可能な JSON を抽出するには、ドキュメントの実際の構成内容に応じて異なるアプローチが必要になります。具体的には、キーと値のパターンを持つプレーンテキスト、行と列を持つテーブル、入力可能なフォームフィールド、または OCR が必要なスキャン画像などです。

この記事では、Spire.PDF for .NET を使用した実行可能な C# コードで、これら 4 つのシナリオすべてを解説しています。実際の請求書を JSON に変換するコンバーターを構築し、結合セルやヘッダーの欠落といった一般的なテーブル抽出の問題に対応し、あらゆる .NET プロジェクトに組み込み可能な再利用可能な PdfToJsonConverter クラスにまとめ上げています。

目次

  1. 「PDF から JSON への変換」の実際の意味
  2. Spire.PDF for .NET のインストール
  3. C#で PDF テキストを JSON に変換
  4. C#で PDF テーブルを JSON に変換
  5. PDF フォームフィールドを JSON に変換
  6. 請求書 PDF を JSON に変換:実務例
  7. 複数の PDF を JSON に一括変換
  8. C#で PDF から JSON への変換クラスを構築
  9. C#で OCR 出力を JSON に変換
  10. パフォーマンスに関する考慮事項
  11. よくある質問

1. 「PDF から JSON への変換」の実際の意味

CSV を JSON に変換するような、ビルトインの「PDF から JSON」変換機能は存在しません。PDF には JSON 構造がありません。開発者が実際に必要としているのは、PDF からコンテンツを抽出し、そのコンテンツを用途に合った JSON フォーマットに整形することです。

PDF の種類と業務要件に応じて、出力先の JSON は次の 3 つのカテゴリのいずれかに分類されます。

生テキスト JSON

各ページからすべてのテキストを抽出し、JSON のラッパーで包みます。検索インデックス、RAG パイプライン、ドキュメントアーカイブに適しています。

{
  "sourceFile": "契約書.pdf",
  "pages": [
    { "pageNumber": 1, "text": "業務委託契約書\n株式会社テクノソリューションズと..." }
  ]
}

キーと値の JSON

多くの PDF はラベル: 値のパターンに従っています。従業員記録、登録フォーム、簡易請求書などです。ここでは、これらのペアをフラットな JSON オブジェクトにパースすることが目標になります。

{
  "name": "田中 太郎",
  "email": "tanaka@ technosol.co.jp",
  "department": "開発部",
  "employeeId": "EMP-2026-0142"
}

構造化ビジネス JSON

実際のビジネスドキュメントにはネストされたデータが含まれます。請求書にはヘッダー、明細行、税額内訳、支払条件などがあります。JSON 出力もその構造を反映する必要があります。

{
  "invoiceNumber": "INV-2026-0042",
  "vendor": "株式会社テクノソリューションズ",
  "date": "2026-06-15",
  "lineItems": [
    { "description": "部品A", "quantity": 150, "unitPrice": 24.50, "total": 3675.00 }
  ],
  "subtotal": 3675.00,
  "tax": 294.00,
  "total": 3969.00
}

この区別が重要です。 「PDF を JSON に変換」と検索する際は、アプリケーションにどの出力フォーマットが必要かを判断する必要があります。この記事の以降のセクションでは、C#で Spire.PDF を使用して各種フォーマットを構築する方法を説明します。


2. Spire.PDF for .NET のインストール

NuGet パッケージマネージャーコンソールからインストールします。

Install-Package Spire.PDF

または、.csprojに追加します。

<PackageReference Include="Spire.PDF" Version="*" />

プロジェクトに以下の名前空間を追加します。

using Spire.Pdf;
using Spire.Pdf.Texts;
using Spire.Pdf.Utilities;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Encodings.Web;
using System.Text.RegularExpressions;

Spire.PDF は .NET Framework、.NET Core、および .NET 6/7/8/9+ をサポートしています。


3. C#で PDF テキストを JSON に変換

最も一般的な出発点として、PDF からテキストを抽出して JSON 出力を生成します。

PDF からテキストを抽出

using Spire.Pdf;
using Spire.Pdf.Texts;
using System.Collections.Generic;
using System.Text.RegularExpressions;

using (PdfDocument pdf = new PdfDocument())
{
    pdf.LoadFromFile("従業員記録.pdf");

    var pages = new List<Dictionary<string, string>>();

    for (int i = 0; i < pdf.Pages.Count; i++)
    {
        PdfPageBase page = pdf.Pages[i];

        // PdfTextExtractOptions を使用して全テキストを抽出
        PdfTextExtractOptions options = new PdfTextExtractOptions();
        options.IsExtractAllText = true;

        PdfTextExtractor extractor = new PdfTextExtractor(page);
        string pageText = extractor.ExtractText(options);
        pageText = Regex.Replace(pageText, @" {2,}", "\n");

        pages.Add(new Dictionary<string, string>
        {
            { "pageNumber", (i + 1).ToString() },
            { "text", pageText.Trim() }
        });
    }
}

キーと値のペアを JSON にパース

PDF がラベル: 値のパターンに従っている場合、抽出したテキストを構造化フィールドにパースします。

using System.Text.Json;
using System.Text.Encodings.Web;

var parsedFields = new Dictionary<string, string>();

foreach (var page in pages)
{
    string[] lines = page["text"].Split('\n');
    foreach (string line in lines)
    {
        int colonIndex = line.IndexOf(':');
        if (colonIndex > 0)
        {
            string key = line.Substring(0, colonIndex).Trim();
            string value = line.Substring(colonIndex + 1).Trim();
            parsedFields[key] = value;
        }
    }
}

// JSON シリアライゼーションのオプションを設定
var jsonOptions = new JsonSerializerOptions
{
    WriteIndented = true,
    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
    Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
};

string jsonOutput = JsonSerializer.Serialize(parsedFields, jsonOptions);
File.WriteAllText("従業員記録.json", jsonOutput);

主要な API 呼び出し

  • PdfDocument.LoadFromFile() — PDF ファイルを開きます
  • PdfTextExtractor.ExtractText() — ページからテキストコンテンツを抽出します
  • PdfTextExtractOptions.IsExtractAllText — 空白と書式を保持します

出力

以下は、抽出された従業員記録から生成された構造化 JSON の例です。

{
  "name": "田中 太郎",
  "email": "tanaka@ technosol.co.jp",
  "department": "開発部",
  "employeeId": "EMP-2026-0142",
  "startDate": "2024-03-15"
}

以下のスクリーンショットは、サンプルを実行後に生成された実際の JSON ファイルを示しています。

C#で PDF テキストを JSON に変換

このアプローチは、フォーム、記録、一貫したキーと値のレイアウトを持つドキュメントに効果的です。構造化されていないテキストの場合は、パース処理をスキップして生のページを直接シリアル化してください。

PDF テキスト抽出について詳しく知りたい場合は、Spire.PDF for .NET を使用した C#での PDF テキスト抽出 の専用ガイドを参照してください。


4. C#で PDF テーブルを JSON に変換

前セクションでは PDF からのプレーンテキスト抽出に焦点を当てました。段落やシンプルな記録には効果的ですが、多くのビジネスドキュメントでは最も重要な情報がテーブルに整理されています。例えば、請求書の明細行、売上報告書、財務諸表などです。行、列、セル間の関係を保持するには、テーブルデータを構造化 JSON に変換する前に、異なる方法で抽出する必要があります。

テーブル抽出がテキスト抽出と異なる理由

テキスト抽出は、読み取り順にフラットな文字列ストリームを返します。テーブルがページ上で完全に整理されて見えても、抽出されたテキストは行と列の構造を失うことが多く、どの値が一緒に属するのかを識別するのが困難になります。

テーブルのレイアウトを保持するには、専用のテーブル抽出エンジンが必要です。PdfTableExtractor はページレイアウトを分析し、テーブルの境界を検出し、行ごと・セルごとに反復処理可能な PdfTable オブジェクトを返します。以下のようなフラットな文字列を生成するのではなく、

部品A 150 ¥2,450 ¥367,500

次のような構造化 JSON を生成できます。

{
  "Product": "部品A",
  "Quantity": "150",
  "Unit Price": "¥2,450",
  "Total": "¥367,500"
}

以下のサンプルでは、PDF からテーブルを抽出して JSON にシリアル化する方法を示します。

PDF からテーブルを抽出

using Spire.Pdf;
using Spire.Pdf.Utilities;
using System.Collections.Generic;

using (PdfDocument pdf = new PdfDocument())
{
    pdf.LoadFromFile("売上報告書.pdf");

    // PdfTableExtractor を初期化してテーブルを抽出
    PdfTableExtractor tableExtractor = new PdfTableExtractor(pdf);
    var allTables = new List<List<List<string>>>();

    for (int pageIndex = 0; pageIndex < pdf.Pages.Count; pageIndex++)
    {
        PdfTable[] tables = tableExtractor.ExtractTable(pageIndex);

        if (tables != null && tables.Length > 0)
        {
            foreach (PdfTable table in tables)
            {
                int rowCount = table.GetRowCount();
                int colCount = table.GetColumnCount();
                var tableData = new List<List<string>>();

                for (int row = 0; row < rowCount; row++)
                {
                    var rowData = new List<string>();
                    for (int col = 0; col < colCount; col++)
                    {
                        rowData.Add(table.GetText(row, col).Trim());
                    }
                    tableData.Add(rowData);
                }

                allTables.Add(tableData);
            }
        }
    }
}

テーブルデータを JSON にシリアル化

var jsonTables = new List<object>();

foreach (var tableData in allTables)
{
    if (tableData.Count < 2) continue;

    var headers = tableData[0];
    var rows = new List<Dictionary<string, string>>();

    for (int i = 1; i < tableData.Count; i++)
    {
        var rowObj = new Dictionary<string, string>();
        for (int j = 0; j < headers.Count && j < tableData[i].Count; j++)
        {
            rowObj[headers[j]] = tableData[i][j];
        }
        rows.Add(rowObj);
    }

    jsonTables.Add(new
    {
        tableIndex = allTables.IndexOf(tableData) + 1,
        headers = headers,
        data = rows
    });
}

// JSON にシリアル化してファイルに保存
string tableJson = JsonSerializer.Serialize(new
{
    sourceFile = "売上報告書.pdf",
    tables = jsonTables
}, new JsonSerializerOptions { WriteIndented = true, Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping });

File.WriteAllText("売上報告書_テーブル.json", tableJson);

主要な API 呼び出し

  • PdfTableExtractor(PdfDocument) — テーブル抽出エンジンを初期化します
  • PdfTableExtractor.ExtractTable(pageIndex) — ページからテーブルを検出して抽出します
  • PdfTable.GetRowCount() / GetColumnCount() — テーブルの寸法を返します
  • PdfTable.GetText(row, col) — セルの内容を読み取ります

JSON 出力サンプル

生成された JSON は、検出された列ヘッダーに基づいて各行をキーと値のペアに整理することで、元のテーブル構造を保持します。

{
  "sourceFile": "売上報告書.pdf",
  "tables": [
    {
      "tableIndex": 1,
      "headers": ["商品名", "数量", "単価", "合計"],
      "data": [
        { "商品名": "部品A", "数量": "150", "単価": "¥2,450", "合計": "¥367,500" },
        { "商品名": "部品B", "数量": "80", "単価": "¥3,990", "合計": "¥319,200" }
      ]
    }
  ]
}

以下のスクリーンショットは、サンプルを実行後に生成された実際の JSON ファイルを示しています。

C#で PDF テーブルを JSON に変換

このアプローチは、請求書、報告書、その他明確なテーブル構造を持つ PDF に効果的です。結合セル、ヘッダーの欠落、複数ページにまたがるテーブルを含むドキュメントの場合は、追加の後処理が必要になることがあります。

PDF テーブル抽出について詳しく知りたい場合は、Spire.PDF for .NET を使用した C#での PDF テーブル抽出 の専用ガイドを参照してください。

よくあるテーブル抽出の問題

実際の PDF テーブルは整然としていません。ここでは最も頻繁に遭遇する 3 つの問題とその対処方法を紹介します。

問題 1:ヘッダーの欠落

多くの請求書や報告書には、明示的なヘッダー行のないテーブルが含まれています。データがすぐに始まります。

りんご      10    ¥299    ¥2,990
みかん      5     ¥150    ¥750

最初の行がヘッダーではなくデータの場合、既知のスキーマに基づいて列名を手動で割り当てます。

// PDFテーブルにヘッダー行がない場合はヘッダーを定義
string[] defaultHeaders = { "商品名", "数量", "単価", "合計" };

var rows = new List<Dictionary<string, string>>();
for (int i = 0; i < tableData.Count; i++)  // 1ではなく0から開始
{
    var rowObj = new Dictionary<string, string>();
    for (int j = 0; j < defaultHeaders.Length && j < tableData[i].Count; j++)
    {
        rowObj[defaultHeaders[j]] = tableData[i][j];
    }
    rows.Add(rowObj);
}

問題 2:結合セル

財務報告書のテーブルには、グループ化のために結合セルが使われていることがあります。

四半期    売上高       費用
Q1        ¥12,000,000  ¥9,500,000
          ¥11,500,000  ¥8,800,000
Q2        ¥14,000,000  ¥10,200,000

抽出エンジンは結合セルに対して空の文字列を返します。直前の空でない値から前方に埋めていきます。

// 結合セルを前行の値で埋める
for (int col = 0; col < headers.Count; col++)
{
    string lastValue = "";
    for (int row = 1; row < tableData.Count; row++)
    {
        if (col < tableData[row].Count && !string.IsNullOrWhiteSpace(tableData[row][col]))
        {
            lastValue = tableData[row][col];
        }
        else if (col < tableData[row].Count)
        {
            tableData[row][col] = lastValue;
        }
    }
}

問題 3:複数ページにまたがるテーブル

エンタープライズレポートでは、1 つのテーブルが複数ページにまたがり、各ページでヘッダー行が繰り返されることがあります。シリアル化時にヘッダーの重複を排除することで対応します。

var combinedRows = new List<Dictionary<string, string>>();
string[] expectedHeaders = null;

for (int pageIndex = 0; pageIndex < pdf.Pages.Count; pageIndex++)
{
    PdfTable[] tables = tableExtractor.ExtractTable(pageIndex);
    if (tables == null) continue;

    foreach (PdfTable table in tables)
    {
        for (int r = 0; r < table.GetRowCount(); r++)
        {
            var cells = new List<string>();
            for (int c = 0; c < table.GetColumnCount(); c++)
            {
                cells.Add(table.GetText(r, c).Trim());
            }

            // 最初のページの最初の行をヘッダーにする
            if (expectedHeaders == null && r == 0)
            {
                expectedHeaders = cells.ToArray();
                continue;
            }

            // 後続ページの重複ヘッダー行をスキップ
            if (r == 0 && cells.SequenceEqual(expectedHeaders))
                continue;

            var rowDict = new Dictionary<string, string>();
            for (int c = 0; c < expectedHeaders.Length && c < cells.Count; c++)
            {
                rowDict[expectedHeaders[c]] = cells[c];
            }
            combinedRows.Add(rowDict);
        }
    }
}

5. PDF フォームフィールドを JSON に変換

プレーンテキストやテーブルとは異なり、入力可能な PDF フォームはすでに名前付きフィールドとしてデータを保存しています。申請書、アンケート、登録フォームにはフィールド名と値が含まれており、JSON のキーと値のペアに直接マッピングできます。そのため、フォームデータは PDF コンテンツの中で最もシリアル化が容易な種類の 1 つです。

フォームフィールドの読み取りとエクスポート

using Spire.Pdf;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System.Collections.Generic;

using (PdfDocument pdf = new PdfDocument())
{
    pdf.LoadFromFile("登録フォーム.pdf");

    // PdfFormWidget を使用してフォームフィールドにアクセス
    PdfFormWidget formWidget = pdf.Form as PdfFormWidget;
    var formData = new Dictionary<string, object>();

    if (formWidget != null)
    {
        for (int i = 0; i < formWidget.FieldsWidget.List.Count; i++)
        {
            PdfField field = formWidget.FieldsWidget.List[i] as PdfField;

            if (field is PdfTextBoxFieldWidget textBox)
                formData[textBox.Name] = textBox.Text;
            else if (field is PdfCheckBoxWidgetFieldWidget checkBox)
                formData[checkBox.Name] = checkBox.Checked;
            else if (field is PdfRadioButtonListFieldWidget radioButton)
                formData[radioButton.Name] = radioButton.Value;
            else if (field is PdfComboBoxWidgetFieldWidget comboBox)
                formData[comboBox.Name] = comboBox.SelectedValue;
            else if (field is PdfListBoxWidgetFieldWidget listBox)
            {
                var selectedItems = new List<string>();
                foreach (PdfListWidgetItem item in listBox.Values)
                    selectedItems.Add(item.Value);
                formData[listBox.Name] = selectedItems;
            }
        }
    }

    var formOutput = new
    {
        sourceFile = "登録フォーム.pdf",
        fieldCount = formData.Count,
        fields = formData
    };

    string json = JsonSerializer.Serialize(formOutput, new JsonSerializerOptions
    {
        WriteIndented = true,
        Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping
    });

    File.WriteAllText("登録フォーム_データ.json", json);
}

主要な API 呼び出し

  • PdfFormWidget — ドキュメントのインタラクティブフォームへのアクセスを提供します
  • PdfTextBoxFieldWidget.Text — テキスト入力値を読み取ります
  • PdfCheckBoxWidgetFieldWidget.Checked — チェックボックスの状態を読み取ります
  • PdfRadioButtonListFieldWidget.Value — 選択されたラジオボタンを読み取ります
  • PdfComboBoxWidgetFieldWidget.SelectedValue — コンボボックスの選択値を読み取ります

出力

以下は、抽出されたフォームフィールドが構造化 JSON として表現される例です。

{
  "sourceFile": "登録フォーム.pdf",
  "fieldCount": 6,
  "fields": {
    "FullName": "田中 太郎",
    "Email": "tanaka@ technosol.co.jp",
    "Department": "営業部",
    "AgreeTerms": true,
    "SubscriptionPlan": "エンタープライズ",
    "Skills": ["C#", "SQL", "Azure"]
  }
}

以下のスクリーンショットは、フォームデータをエクスポートした後に生成された実際の JSON ファイルを示しています。

C#で PDF フォームフィールドを JSON に変換

このアプローチは、テキストボックス、チェックボックス、ラジオボタン、ドロップダウンリストなどの構造化フィールドを含むインタラクティブ PDF フォームに効果的です。各フィールドにはすでに一意の名前が付いているため、抽出されたデータは追加のパース処理なしで直接 JSON にシリアル化できます。


6. 請求書 PDF を JSON に変換:実務例

請求書処理は、PDF から JSON への変換において最も一般的なビジネスユースケースの 1 つです。このセクションでは、完全なパーサー実装を示すのではなく、セクション 3 と 4 で学んだ抽出技術を組み合わせて実際の問題を解決する方法を紹介します。

目標 JSON 構造

抽出コードを記述する前に、ターゲットスキーマを定義します。一般的な請求書の場合、JSON 出力は以下のようになります。

{
  "invoiceNumber": "INV-2026-0042",
  "date": "2026-06-15",
  "vendor": "株式会社テクノソリューションズ",
  "paymentTerms": "末締め翌月30日払い",
  "lineItems": [
    { "description": "部品A", "quantity": 150, "unitPrice": 24.50, "total": 3675.00 },
    { "description": "部品B", "quantity": 80, "unitPrice": 39.90, "total": 3192.00 }
  ],
  "subtotal": 8367.00,
  "tax": 669.36,
  "total": 9036.36
}

抽出パターン

テキスト抽出(セクション 3)を使用してヘッダーフィールドを正規表現でパースし、テーブル抽出(セクション 4)を使用して明細行を取得します。

// 正規表現を使用して抽出テキストからヘッダーフィールドをパース
invoice["invoiceNumber"] = Regex.Match(fullText, @"請求書番号:\s*(\S+)").Groups[1].Value;
invoice["date"] = Regex.Match(fullText, @"日付:\s*(\S+)").Groups[1].Value;
invoice["vendor"] = Regex.Match(fullText, @"取引先:\s*(.+)").Groups[1].Value;

// テーブルデータから明細行を抽出(セクション4のパターン)
for (int r = 1; r < table.GetRowCount(); r++)
{
    lineItems.Add(new
    {
        description = table.GetText(r, 0).Trim(),
        quantity = int.Parse(table.GetText(r, 1).Trim()),
        unitPrice = ParseCurrency(table.GetText(r, 2)),
        total = ParseCurrency(table.GetText(r, 3))
    });
}

この実装では、セクション 3 で紹介したテキスト抽出とセクション 4 で紹介したテーブル抽出を組み合わせています。正規表現はシンプルなフィールドマッチングにのみ使用しており、PDF 処理の中心はすべて Spire.PDF の API に依存しています。

異なる請求書レイアウトへの対応

本番環境では、単一の請求書フォーマットだけを扱うことはほとんどありません。

  • 固定テンプレート+正規表現 — 請求書の発行元を管理している場合や、既知の取引先からの請求書を処理する場合に有効
  • テンプレートマッチング — 取引先ごとに正規表現パターンを管理
  • AI 活用による抽出 — 未知または多様なレイアウトの場合、OCR 出力を LLM と組み合わせる

正規表現ベースのパースは、既知のフォーマットに対して高速かつ信頼性が高いです。本番環境での実装には、セクション 8 の PdfToJsonConverter クラスを拡張して、同じ抽出パターンを再利用する専用請求書パーサーを構築してください。


7. 複数の PDF を JSON に一括変換

本番ワークフローでは、数百・数千の PDF を一度に処理します。このバッチプロセッサはエラーを適切に処理し、結果をログに記録します。

using Spire.Pdf;
using Spire.Pdf.Texts;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;

string inputDir = @"C:\PDFs\請求書";
string outputDir = @"C:\Output\JSON";
Directory.CreateDirectory(outputDir);

string[] pdfFiles = Directory.GetFiles(inputDir, "*.pdf");
var results = new List<object>();

foreach (string pdfPath in pdfFiles)
{
    string fileName = Path.GetFileNameWithoutExtension(pdfPath);
    string outputPath = Path.Combine(outputDir, $"{fileName}.json");

    try
    {
        using (PdfDocument pdf = new PdfDocument())
        {
            pdf.LoadFromFile(pdfPath);
            var pageTexts = new List<string>();

            for (int i = 0; i < pdf.Pages.Count; i++)
            {
                var extractor = new PdfTextExtractor(pdf.Pages[i]);
                var options = new PdfTextExtractOptions { IsExtractAllText = true };
                pageTexts.Add(extractor.ExtractText(options).Trim());
            }

            var doc = new
            {
                sourceFile = Path.GetFileName(pdfPath),
                pageCount = pdf.Pages.Count,
                processedAt = DateTime.UtcNow,
                content = pageTexts
            };

            File.WriteAllText(outputPath, JsonSerializer.Serialize(doc,
                new JsonSerializerOptions { WriteIndented = true }));

            results.Add(new { file = fileName, status = "success" });
        }
    }
    catch (Exception ex)
    {
        results.Add(new { file = fileName, status = "error", error = ex.Message });
    }
}

// バッチ処理の結果をログファイルに保存
File.WriteAllText(Path.Combine(outputDir, "_log.json"),
    JsonSerializer.Serialize(results, new JsonSerializerOptions { WriteIndented = true }));

バッチ処理の対象が請求書の場合はテキストのみの抽出をセクション 6 の請求書 JSON 抽出パターンに置き換え、汎用的な変換にはセクション 8 の PdfToJsonConverter クラスを使用してください。


8. C#で PDF から JSON への変換クラスを構築

本番アプリケーションでは、すべての抽出ロジックを 1 つのクラスにカプセル化します。以下の PdfToJsonConverter は、テキスト、テーブル、フォームフィールドの抽出を組み合わせた再利用可能な PDF から JSON への変換クラスです。

using Spire.Pdf;
using Spire.Pdf.Texts;
using Spire.Pdf.Utilities;
using Spire.Pdf.Fields;
using Spire.Pdf.Widget;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;

public class PdfToJsonConverter
{
    private readonly JsonSerializerOptions _jsonOptions = new()
    {
        WriteIndented = true,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
        DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull
    };

    public string ConvertToJson(string pdfPath)
    {
        using (PdfDocument pdf = new PdfDocument())
        {
            pdf.LoadFromFile(pdfPath);

            var result = new
            {
                sourceFile = Path.GetFileName(pdfPath),
                processedAt = DateTime.UtcNow,
                text = ExtractText(pdf),
                tables = ExtractTables(pdf),
                formFields = ExtractFormFields(pdf)
            };

            return JsonSerializer.Serialize(result, _jsonOptions);
        }
    }

    public void ConvertAndSave(string pdfPath, string outputPath)
    {
        File.WriteAllText(outputPath, ConvertToJson(pdfPath));
    }

    // セクション3のテキスト抽出技術(PdfTextExtractor + PdfTextExtractOptions)を再利用
    private List<PageText> ExtractText(PdfDocument pdf) { return new List<PageText>(); }

    // セクション4のテーブル抽出技術(PdfTableExtractor + ExtractTable)を再利用
    private List<TableData> ExtractTables(PdfDocument pdf) { return new List<TableData>(); }

    // セクション5のフォームフィールド抽出技術(PdfFormWidget + フィールド型チェック)を再利用
    private Dictionary<string, object> ExtractFormFields(PdfDocument pdf) { return new Dictionary<string, object>(); }
}

public class PageText
{
    public int PageNumber { get; set; }
    public string Text { get; set; }
}

public class TableData
{
    public int PageNumber { get; set; }
    public int RowCount { get; set; }
    public List<List<string>> Rows { get; set; }
}

使用例

var converter = new PdfToJsonConverter();

// 単一ファイル
converter.ConvertAndSave("請求書レポート.pdf", "請求書レポート.json");

// ASP.NETコントローラー内で使用
[HttpPost("api/pdf-to-json")]
public IActionResult ConvertPdf(IFormFile file)
{
    var tempPath = Path.GetTempFileName();
    file.CopyTo(new FileStream(tempPath, FileMode.Create));
    var converter = new PdfToJsonConverter();
    string json = converter.ConvertToJson(tempPath);
    return Content(json, "application/json");
}

ヘルパーメソッド(ExtractTextExtractTablesExtractFormFields)は、セクション 3〜5 で紹介した抽出技術を再利用しています。完全な実装は各セクションを参照してください。

本番パイプラインのベストプラクティス

PDF から JSON への変換を本番システムに組み込む際は、以下の点に注意してください。

  • まず JSON スキーマを定義する。 抽出コードを記述する前に、各 PDF 要素をターゲットフィールドにマッピングします。
  • 抽出データを検証する。 通貨文字列、日付、ID はシリアル化前にパースして検証する必要があります。
  • 欠落値に対応する。 JsonIgnoreCondition.WhenWritingNull を使用して、null フィールドを出力から省略します。
  • メタデータを含める。 監査のため、ソースファイル名、ページ番号、抽出タイムスタンプを常に記録します。
  • テキストの整形を行う。 抽出された文字列の空白をトリムし、改行を正規化し、エンコーディングの問題を処理します。

9. C#で OCR 出力を JSON に変換

スキャンされた PDF は選択可能なテキストではなく画像を含んでいるため、JSON に変換する前に OCR エンジンで処理する必要があります。Spire.PDF は PDF のレンダリングとページ処理を担当し、テキスト認識は Tesseract や Azure AI Vision などの OCR ソリューションで実行します。

詳しい手順については、C#でスキャン PDF からテキストを抽出する方法 を参照してください。

OCR が認識テキストを返した後は、この記事で前述した同じ技術を使用してパースできます。

OCR テキストを JSON にパース

string recognizedText = ocrEngine.Recognize(imagePath);

// 前述のサンプルで示したヘルパーメソッドを使用して認識テキストをパース
var parsedData = ParseRecognizedText(recognizedText);

string json = JsonSerializer.Serialize(parsedData, new JsonSerializerOptions
{
    WriteIndented = true
});

ベストプラクティス

  • OCR 精度を高めるため、ドキュメントを300 DPI 以上でスキャンします。
  • 請求書番号、日付、通貨金額などの重要なフィールドは、シリアル化前に検証します。
  • 一貫した JSON 構造を構築するため、この記事で紹介したパースパターンを再利用します。

10. パフォーマンスに関する考慮事項

PDF から JSON への変換は、5 ページの単一ドキュメントであれば問題なく動作します。しかし本番環境では、数百ページにおよぶファイルを数百件処理することになります。ここでは実際に直面する問題について説明します。

大規模 PDF(100 ページ以上)

シリアル化する前にすべてのページテキストを List<string> に読み込まないでください。各ページを段階的に処理して書き込みます。

using (var stream = File.Create("output.json"))
using (var writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true }))
{
    writer.WriteStartObject();
    writer.WriteString("sourceFile", Path.GetFileName(pdfPath));
    writer.WriteStartArray("pages");

    for (int i = 0; i < pdf.Pages.Count; i++)
    {
        var extractor = new PdfTextExtractor(pdf.Pages[i]);
        var options = new PdfTextExtractOptions { IsExtractAllText = true };
        string text = extractor.ExtractText(options).Trim();

        writer.WriteStartObject();
        writer.WriteNumber("pageNumber", i + 1);
        writer.WriteString("text", text);
        writer.WriteEndObject();
    }

    writer.WriteEndArray();
    writer.WriteEndObject();
}

Utf8JsonWriter はメモリ内に文字列を構築するのではなく、直接ストリームに書き込みます。500 ページのドキュメントの場合、JsonSerializer.Serialize() と比較してピーク時のメモリ使用量を 60〜70% 削減できます。

メモリ使用量

PdfDocument はパースされたページツリー、フォント、画像参照をメモリに保持します。2 つのルールを守ってください。

  1. 常に PdfDocumentusing でラップする — Dispose 時にアンマネージドリソースを解放します
  2. 一度に 1 つのドキュメントのみ処理する — 十分な RAM がない限り、複数の PdfDocument インスタンスを同時に開かないでください

1000 件以上のファイルを処理するバッチジョブでは、ループ内の using パターンにより、各ドキュメントが次の読み込み前に完全に解放されます。

並列処理

バッチ変換は CPU バウンドであり、並列化に適しています。

var pdfFiles = Directory.GetFiles(inputDir, "*.pdf");

Parallel.ForEach(pdfFiles,
    new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount },
    pdfPath =>
{
    string outputPath = Path.Combine(outputDir,
        Path.GetFileNameWithoutExtension(pdfPath) + ".json");
    var converter = new PdfToJsonConverter();
    converter.ConvertAndSave(pdfPath, outputPath);
});

各スレッドは独自の PdfToJsonConverterPdfDocument インスタンスを作成します。PdfDocument はスレッドセーフではないため、1 つのインスタンスを複数スレッド間で共有しないでください。

ストリーミング JSON を使用するタイミング

JsonSerializer.Serialize() ではなく Utf8JsonWriter を使用するべきケース:

  • 出力 JSON が 50 MB を超える場合
  • 200 ページ以上の PDF を処理する場合
  • メモリ制約のある環境(512 MB 制限のコンテナなど)で実行する場合

小規模なドキュメントには JsonSerializer の方がシンプルで、メモリの差は無視できます。


11. よくある質問

C#で PDF を JSON に無料で変換できますか?

Spire.PDF for .NET にはページ制限のある無料評価版があります。本番利用では、30 日間の無料ライセンスを申請 するか、商用ライセンスを購入できます。System.Text.Json シリアライザーは .NET に組み込まれており無料です。

スキャンされた PDF を JSON に変換できますか?

はい、ただし外部の OCR エンジンが必要です。Spire.PDF は SaveAsImage() を通じて PDF ページを画像としてレンダリングし、それを Tesseract、Azure Computer Vision、Amazon Textract などのテキスト認識サービスに渡します。認識されたテキストはその後パース・シリアル化されて JSON になります。統合パターンについては セクション 9 を参照してください。

PDF テーブルを自動的に JSON に変換できますか?

はい。PdfTableExtractor は手動設定なしで各ページのテーブル構造を自動検出します。Word や Excel で作成された適切に構造化されたテーブルと、行と列に見えるように配置されたテキストの視覚的テーブルの両方に対応しています。複数ページのテーブルやヘッダーのないテーブルについては、セクション 4 の処理パターンを参照してください。

複数の PDF を JSON に一括変換できますか?

はい。Directory.GetFiles() でディレクトリを反復処理し、各 PDF を Spire.PDF の抽出 API で処理し、個別の JSON ファイルとして保存します。1 つのファイルが失敗してもバッチ全体が止まらないよう、エラー処理を含めてください。完全なサンプルは セクション 7 を参照してください。

大きな PDF ファイルを C#で JSON に変換するにはどうすればよいですか?

すべてのコンテンツを一度にメモリに読み込むのではなく、ページ単位で PDF を処理します。非常に大きなファイル(100 ページ以上)の場合、Utf8JsonWriter を使用して JSON をストリームに段階的に書き込み、メモリ内で全体の出力を構築しないようにします。ストリーミング JSON パターンと並列処理アプローチについては セクション 10 を参照してください。

API を使用して PDF を JSON に変換できますか?

はい。この記事の PdfToJsonConverter クラスを ASP.NET Web API エンドポイントにラップできます。PDF のアップロードを受け取り、抽出を実行し、JSON レスポンスを返します。Spire.PDF はあらゆる .NET ホスティング環境で動作します。ASP.NET Core、Azure Functions、AWS Lambda、または自己ホストのコンソールアプリなどです。ASP.NET コントローラーのサンプルは セクション 8 を参照してください。


まとめ

PDF から JSON への変換は単一の操作ではありません。ドキュメントに応じて、次の 3 つの異なる問題のいずれかを解決することになります。生テキストを JSON ラッパーで包むこと、キーと値のパターンをフラットなオブジェクトにパースすること、またはテキストとテーブルの抽出から構造化ビジネス JSON を構築することです。

この記事では、これら 3 つすべてに加えて、単純な実装を破綻させる複雑な要素についても解説しました。ヘッダーのないテーブル、結合セル、複数ページのテーブル、入力可能なフォームフィールド、さまざまな請求書レイアウト、バッチ処理、大規模ドキュメントのメモリ管理、OCR の統合境界などです。

PdfToJsonConverter クラスは、お使いのドキュメントタイプに合わせてカスタマイズできる出発点です。セクション 6 で示した請求書抽出パターンは、実際のビジネスドキュメントに対してこれらの技術を組み合わせる方法を示しています。どちらも Spire.PDF for .NET を使用しており、外部依存なしにローカルですべての PDF 読み取りを処理します。

始めるには:

Read 15 times