【実務・中級編】【上級者向け】表のレイアウトを外部設定ファイル(JSON)から読み込み、動的生成するテンプレートエンジン – Word VBA解析バイブル

スポンサーリンク
スポンサーリンク

脱・ハードコーディング:Word VBAにおける「テンプレートエンジン」の実装戦略

業務自動化の現場において、多くの開発者が陥る「底なし沼」がある。それは、「帳票のレイアウト変更のたびに、VBAのソースコードを書き換えている」という事態だ。

「列を一つ増やしてほしい」「このセルの幅を5mm広げてほしい」「フォントをMS明朝から游明朝に変えてほしい」……。クライアントや現場からのこれらの要求は、一見些細なものに見える。しかし、これらがすべてVBAのロジック内にハードコーディング(直書き)されていた場合、それは単なる「修正作業」ではなく、「再コンパイルと再配布を伴う、極めてコストの高いデプロイ作業」へと変貌する。

本記事では、そのような設計の脆弱性を打破し、Word VBAを単なる「マクロ」から、プロフェッショナルな「帳票生成エンジン」へと昇華させるための、JSONを用いた動的テーブル生成アーキテクチャを伝授する。

1. 設計思想:ロジックとデータの「完全なる分離」

一流のアーキテクトは、プログラムを書く前に「何が変わり得るか」を定義する。

帳票生成において、以下の2つは明確に分離されるべきだ。
1. エンジン(VBA): 表を作成し、スタイルを適用し、データを流し込む「手順」を司る。
2. 定義(JSON): 列数、列幅、罫線の太さ、フォント、背景色などの「見た目」を司る。

この分離(疎結合)を実現することで、VBAのコードを一切触ることなく、外部のJSONファイルを書き換えるだけで、あらゆるレイアウトの帳票を生成することが可能になる。これが「テンプレートエンジン」の正体だ。

2. テンプレートの定義(JSON Schema)

まずは、エンジンの「設計図」となるJSONの構造を定義する。ここでは、単なる列数だけでなく、各列の幅や、ヘッダー部分のスタイルまで制御できるように設計する。

{
  "tableConfig": {
    "rows": 3,
    "cols": 3,
    "columnWidths": [50, 150, 100],
    "headerStyle": {
      "fontSize": 11,
      "bold": true,
      "backgroundColor": "RGB(220, 220, 220)",
      "fontName": "游明朝"
    },
    "defaultStyle": {
      "fontSize": 10,
      "fontName": "游明朝"
    },
    "borderStyle": {
      "lineWidth": 0.5,
      "color": "RGB(0, 0, 0)"
    }
  }
}

3. 実装:堅牢な動的生成エンジン

VBAでJSONを扱うには、[VBA-JSON](https://github.com/VBA-tools/VBA-JSON) などのライブラリを使用するのが業界標準だ。ここでは、そのライブラリを用いてJSONをパースし、Wordの `Table` オブジェクトを制御するプロフェッショナルな実装例を示す。

【重要】 プロダクションコードにおいて、`Selection` オブジェクトは厳禁だ。`Selection` はユーザーのカーソル位置に依存し、動作が不安定になるだけでなく、描画パフォーマンスを著しく低下させる。必ず `Range` オブジェクトを起点に操作せよ。

Option Explicit

' --------------------------------------------------------------------------------
' 依存関係: VBA-JSON (Tim Hall氏作) をプロジェクトに導入していること
' --------------------------------------------------------------------------------

''' <summary>
''' JSON定義に基づき、Word文書内に動的なテーブルを生成する。
''' </summary>
''' <param name="jsonString">JSON形式の設定文字列</param>
''' <param name="targetRange">テーブルを挿入する範囲</param>
Public Sub GenerateDynamicTable(ByVal jsonString As String, ByRef targetRange As Range)
    Dim json As Object
    Dim config As Object
    Dim headerStyle As Object
    Dim defaultStyle As Object
    Dim borderStyle As Object
    
    Dim tbl As Table
    Dim i As Integer, j As Integer
    Dim colWidths As Variant
    
    ' 1. JSONのパース
    On Error GoTo ErrorHandler
    Set json = JsonConverter.ParseJson(jsonString)
    Set config = json("tableConfig")
    
    ' 2. テーブルの初期生成
    ' SelectionではなくRangeを使用することで、処理の高速化と安定性を確保
    Set tbl = targetRange.Tables.Add(Range:=targetRange, _
                                     NumRows:=config("rows"), _
                                     NumColumns:=config("cols"))
    
    ' 3. レイアウト設定(列幅・罫線)
    tbl.AllowAutoFit = False ' 自動調整をオフにしないと、指定した幅が無視される
    colWidths = config("columnWidths")
    
    For i = 1 To config("cols")
        tbl.Columns(i).Width = colWidths(i)
    Next i
    
    ' 罫線の適用
    Set borderStyle = config("borderStyle")
    With tbl.Borders
        .InsideLineStyle = wdLineStyleSingle
        .OutsideLineStyle = wdLineStyleSingle
        .InsideLineWidth = borderStyle("lineWidth")
        .OutsideLineWidth = borderStyle("lineWidth")
        .InsideColor = ParseRGB(borderStyle("color"))
        .OutsideColor = ParseRGB(borderStyle("color"))
    End With

    ' 4. スタイル適用(ヘッダーおよびデフォルト)
    Set headerStyle = config("headerStyle")
    Set defaultStyle = config("defaultStyle")

    For i = 1 To config("rows")
        For j = 1 To config("cols")
            With tbl.Cell(i, j).Range
                ' デフォルトフォント設定
                .Font.Name = defaultStyle("fontName")
                .Font.Size = defaultStyle("fontSize")
                
                ' ヘッダー行(1行目)の個別設定
                If i = 1 Then
                    .Font.Name = headerStyle("fontName")
                    .Font.Size = headerStyle("fontSize")
                    .Font.Bold = headerStyle("bold")
                    tbl.Cell(i, j).Shading.BackgroundPatternColor = ParseRGB(headerStyle("backgroundColor"))
                End If
            End With
        Next j
    Next i

    Debug.Print "Table generation completed successfully."
    Exit Sub

ErrorHandler:
    MsgBox "致命的なエラーが発生しました。JSONの構造を確認してください。" & vbCrLf & _
           "Error: " & Err.Description, vbCritical, "Engine Error"
End Sub

''' <summary>
''' "RGB(r, g, b)" 形式の文字列を、VBAのLong型カラー値に変換する。
''' </summary>
Private Function ParseRGB(ByVal rgbString As String) As Long
    Dim parts As Variant
    Dim r As Integer, g As Integer, b As Integer
    
    ' 文字列から数字部分を抽出 (簡易的な実装)
    rgbString = Replace(Replace(Replace(rgbString, "RGB(", ""), ")", ""), " ", "")
    parts = Split(rgbString, ",")
    
    r = CInt(parts(0))
    g = CInt(parts(1))
    b = CInt(parts(2))
    
    ParseRGB = RGB(r, g, b)
End Function

4. プロフェッショナルが守るべき「極限の知見」

このコードを実務に投入する際、単に動くだけでは不十分だ。以下の3点を意識せよ。

① オブジェクトのライフサイクルとエラーハンドリング

JSONのパースは、外部データに依存するため、最もエラーが起きやすい箇所だ。`On Error GoTo` を用いて、エラー発生時に「どこで、何が原因で」失敗したかをログに残すか、ユーザーに明確な指示を出す設計にせよ。また、`tbl` オブジェクトが正しく生成されなかった場合に、後続の処理が NULL を参照してクラッシュしないよう、オブジェクトの存在チェックを徹底すること。

② パフォーマンスの重み:`AllowAutoFit` の罠

Wordのテーブル操作において、最もパフォーマンスを阻害し、かつ挙動を不安定にするのが「自動幅調整」だ。`tbl.AllowAutoFit = False` を明示的に指定せずに列幅を指定すると、Wordが勝手にレイアウトを再計算し、指定した数値が無視されることが多々ある。これはエンジニアリングにおける「予期せぬ副作用」の典型例だ。

③ スケーラビリティ:データの流し込み

本記事ではレイアウトの生成に焦点を当てたが、実務ではこの後に「データの流し込み」が続く。
データもまた、JSONやデータベースから取得した「配列」や「コレクション」として受け取り、`tbl.Cell(i, j).Range.Text = data(i)(j)` のように、インデックスベースで直接流し込む設計にせよ。ループの中で `Selection` を動かしてテキストを入力するような実装は、即刻破棄すべき低品質なコードだ。

まとめ

「コードの中に設定を書き込む」という行為は、短期的には速いが、長期的には技術負債を積み上げる行為に他ならない。

JSONによるテンプレート化を実現することで、VBAは「変化する仕様」から解放され、真に堅牢で、保守性の高い、プロダクションレベルのシステムへと進化する。君が構築すべきは、単なる自動化ツールではない。「変化に耐えうる、洗練されたエンジン」である。

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