【テクニカル・上級編】【上級者向け】段落の「書式設定」をJSON形式でシリアライズし、外部システムと連携する – Word VBA解析バイブル

スポンサーリンク

【Word VBAを掌握する極限の知見】段落書式のJSONシリアライズとシステム間連携エンジン

Word VBAにおいて、段落(`Paragraph`)やフォント(`Font`)の書式設定は、場当たり的なコードを書く限りにおいて最も保守性を殺す魔窟である。`Selection`オブジェクトに依存した記述、無駄な`Activate`や`Select`、そして何重にもネストされた`With`ブロック。これらは実行速度を殺すだけでなく、文書テンプレートのバージョン管理を不可能にする。

今回は、Wordの段落が持つ膨大な書式プロパティを抽出し、完全なJSON文字列としてシリアライズ(直列化)、さらにそれを外部システム(Web APIやデータベース)と連携させた上で、別の文書へと完全にデシリアライズして再現する「ポータブル書式管理エンジン」の設計思想と実装を解説する。

1. 建築的アプローチ:なぜWord書式をJSON化するのか?

エンタープライズ環境において、Word文書は単なる「人間が読む成果物」ではなく、「構造化されたデータコンテナ」として扱われるべきだ。しかし、Wordの`.docx`は内部的にXML(OpenXML)で構成されているものの、それを直接操作するのは保守コストが高すぎる。

VBAからWordのネイティブオブジェクト(`Paragraph.Format`や`Font`)にアクセスし、その状態をプレーンなJSONとして外出しできれば、以下の圧倒的なメリットが生まれる。

1. 書式のバージョン管理: Git等の構成管理ツールでWordのスタイル定義をテキストベースで追跡できる。
2. システム間連携: 外部のWeb APIから取得した動的なデザイン定義を、VBA経由で一瞬にしてWord文書へ適用できる。
3. レガシー環境の脱却: ハードコーディングされたマクロの「マジックナンバー(フォントサイズやインデント幅)」を排除し、設定を外部化できる。

2. 実装の核心:シリアライザとメモリ最適化の極意

VBAには標準でJSONパーサ/シリアライザが存在しないため、最小限のネイティブ文字列操作、あるいは信頼できるVBA製JSONライブラリを前提とする(今回はコードのポータビリティを考慮し、VBA単体で完結するStringBuilderパターンと独自JSON構築アプローチを採用する)。

ここで最も重要なのは、WordのCOMオブジェクトへの無駄なアクセスを極限まで減らすことだ。`Paragraph`オブジェクトのプロパティ(`SpaceBefore`, `LineSpacing`, `LeftIndent`等)に何度もドット演算子でアクセスすると、COMの境界を跨ぐオーバーヘッドで実行速度が著しく低下する。

段落書式JSONシリアライザ & デシリアライザ実装

以下のコードは、指定された段落の主要な書式プロパティを抽出してJSON文字列を生成し、逆にJSONから書式を復元するエンジンである。

Option Explicit

‘ ==============================================================================
‘ 担当モジュール: ParagraphStyleSerializer
‘ 概要: Wordの段落書式をJSONにシリアライズし、外部連携および復元を行うエンジン
‘ ==============================================================================

‘ 段落書式をJSON文字列に変換する
Public Function SerializeParagraphFormat(ByVal tgtPara As Paragraph) As String
Dim json As String
Dim fmt As ParagraphFormat
Dim fnt As Font

Set fmt = tgtPara.Format
Set fnt = tgtPara.Range.Font

‘ COMオブジェクトへのアクセスを最小限にするため、ローカル変数にキャッシュ
Dim align As Long: align = fmt.Alignment
Dim spaceBefore As Single: spaceBefore = fmt.SpaceBefore
Dim spaceAfter As Single: spaceAfter = fmt.SpaceAfter
Dim lineSpacing As Single: lineSpacing = fmt.LineSpacing
Dim leftIndent As Single: leftIndent = fmt.LeftIndent
Dim rightIndent As Single: rightIndent = fmt.RightIndent

Dim fontName As String: fontName = fnt.Name
Dim fontSize As Single: fontSize = fnt.Size
Dim fontBold As Long: fontBold = fnt.Bold
Dim fontItalic As Long: fontItalic = fnt.Italic
Dim fontColor As Long: fontColor = fnt.Color

‘ StringBuilderの代わりに効率的な文字列連結を行う
json = “{” & vbCrLf & _
” “”Alignment””: ” & align & “,” & vbCrLf & _
” “”SpaceBefore””: ” & Format(spaceBefore, “0.

“) & “,” & vbCrLf & _

” “”SpaceAfter””: ” & Format(spaceAfter, “0.

“) & “,” & vbCrLf & _

” “”LineSpacing””: ” & Format(lineSpacing, “0.

“) & “,” & vbCrLf & _

” “”LeftIndent””: ” & Format(leftIndent, “0.

“) & “,” & vbCrLf & _

” “”RightIndent””: ” & Format(rightIndent, “0.

“) & “,” & vbCrLf & _

” “”Font””: {” & vbCrLf & _
” “”Name””: “”” & fontName & “””,” & vbCrLf & _
” “”Size””: ” & Format(fontSize, “0.

“) & “,” & vbCrLf & _

” “”Bold””: ” & fontBold & “,” & vbCrLf & _
” “”Italic””: ” & fontItalic & “,” & vbCrLf & _
” “”Color””: ” & fontColor & vbCrLf & _
” }” & vbCrLf & _
“}”

SerializeParagraphFormat = json

‘ オブジェクトの明示的解放(メモリ最適化の鉄則)
Set fmt = Nothing
Set fnt = Nothing
End Function

‘ JSON文字列(簡易パース版)を別の段落に適用するデシリアライザ
Public Sub DeserializeToParagraph(ByVal targetPara As Paragraph, ByVal jsonString As String)
On Error GoTo ErrorHandler

‘ パフォーマンスとメモリ断片化を防ぐため、画面描画とバックグラウンド再計算を停止
Application.ScreenUpdating = False
Application.Calculation = wdCalculationManual

Dim fmt As ParagraphFormat
Dim fnt As Font
Set fmt = targetPara.Format
Set fnt = targetPara.Range.Font

‘ 簡易的なJSON値抽出(本番環境では堅牢なJSONパーサを使用すること)
fmt.Alignment = ExtractNumericFromJson(jsonString, “Alignment”)
fmt.SpaceBefore = ExtractNumericFromJson(jsonString, “SpaceBefore”)
fmt.SpaceAfter = ExtractNumericFromJson(jsonString, “SpaceAfter”)
fmt.LineSpacing = ExtractNumericFromJson(jsonString, “LineSpacing”)
fmt.LeftIndent = ExtractNumericFromJson(jsonString, “LeftIndent”)
fmt.RightIndent = ExtractNumericFromJson(jsonString, “RightIndent”)

fnt.Name = ExtractStringFromJson(jsonString, “Name”)
fnt.Size = ExtractNumericFromJson(jsonString, “Size”)
fnt.Bold = ExtractNumericFromJson(jsonString, “Bold”)
fnt.Italic = ExtractNumericFromJson(jsonString, “Italic”)
fnt.Color = ExtractNumericFromJson(jsonString, “Color”)

CleanUp:
‘ 描画と計算の復元
Application.ScreenUpdating = True
Application.Calculation = wdCalculationAutomatic
Set fmt = Nothing
Set fnt = Nothing
Exit Sub

ErrorHandler:
MsgBox “デシリアライズ中にエラーが発生しました: ” & Err.Description, vbCritical
Resume CleanUp
End Sub

‘ — 内部ヘルパー関数:簡易JSONパサビリティ(正規表現利用) —
Private Function ExtractNumericFromJson(ByVal json As String, ByVal key As String) As Double
Dim regEx As Object
Set regEx = CreateObject(“VBScript.RegExp”)
regEx.Pattern = “””” & key & “””\s:\s(-?\d+(\.\d+)?)'” ‘ 簡易パターンマッチ
regEx.Pattern = “””” & key & “””\s:\s(-?\d+\.?\d)”
regEx.IgnoreCase = True

If regEx.Test(json) Then
ExtractNumericFromJson = CDbl(regEx.Execute(json)(0).SubMatches(0))
Else
ExtractNumericFromJson = 0
End If
Set regEx = Nothing
End Function

Private Function ExtractStringFromJson(ByVal json As String, ByVal key As String) As String
Dim regEx As Object
Set regEx = CreateObject(“VBScript.RegExp”)
regEx.Pattern = “””” & key & “””\s:\s””([^””])”””
regEx.IgnoreCase = True

If regEx.Test(json) Then
ExtractStringFromJson = regEx.Execute(json)(0).SubMatches(0)
Else
ExtractStringFromJson = “”
End If
Set regEx = Nothing
End Function

3. システム間連携:HTTP経由での書式プッシュ・プル

このシリアライザの真価は、生成したJSONをHTTP経由で外部の社内APIサーバーやRDBに送信できる点にある。これにより、「社内標準の文書スタイル定義」をクラウド側で一元管理し、各クライアントのWordマクロが起動時に最新のスタイルを取得して適用する、というモダンなアーキテクチャが構築できる。

以下のコードは、WinHTTPを使用してJSON化された段落書式を外部Web APIへ送信(POST)する実例である。

Public Sub PushStyleToCloud(ByVal tgtPara As Paragraph, ByVal apiEndpoint As String)
Dim jsonPayload As String
jsonPayload = SerializeParagraphFormat(tgtPara)

Dim httpObject As Object
Set httpObject = CreateObject(“MSXML2.ServerXMLHTTP.6.0”)

On Error GoTo ApiError
httpObject.Open “POST”, apiEndpoint, False
httpObject.setRequestHeader “Content-Type”, “application/json; charset=utf-8”
httpObject.setRequestHeader “X-API-Version”, “2.0”

httpObject.send jsonPayload

If httpObject.Status = 200 Then
Debug.Print “スタイルデータの同期に成功しました: ” & httpObject.responseText
Else
Err.Raise 10001, “API連携”, “サーバーがエラーを返しました. ステータス: ” & httpObject.Status
End If

ApiError:
If Err.Number <> 0 Then
MsgBox “通信エラー: ” & Err.Description, vbExclamation
End If
Set httpObject = Nothing
End Sub

4. チーフアーキテクトからの警鐘:メモリリークとパフォーマンスの最適化

VBA開発者の多くは、COMオブジェクトの解放を怠る。`Paragraph`や`Range`、`Font`といったオブジェクト変数は、プロシージャを抜けた瞬間に暗黙的に解放されると勘違いされているが、OfficeアプリケーションのCOM境界においては、明示的な`Set xxx = Nothing`を行わない限り、参照カウントが残存し、メモリリーク(メモリフットプリントの肥大化)を引き起こす。

特に、数百〜数千の段落を一括処理するループ内では、以下のようなアンチパターンは絶対に避けるべきである。

❌ 悪い例(メモリリークの温床)

Dim p As Paragraph
For Each p in ActiveDocument.Paragraphs
‘ オブジェクトを解放せずにループを回すとCOMの参照がゴミ溜まりになる
Debug.Print p.Range.Font.Name
Next p

◯ 正しい例(厳格なライフサイクル管理)

大量の段落を処理する場合は、オブジェクトの生成と破棄をループのスコープ内で厳密に制御するか、あるいはインデックスアクセスを用いてCOM境界を跨ぐ回数を最小限に抑える必要がある。さらに、前述のコードに示した通り、`ScreenUpdating = False` と `Calculation = wdCalculationManual` の併用は、Word VBAのパフォーマンスを劇的に引き上げるための必須要件である。

総括

Wordを単なるワープロソフトとして扱う時代は終わった。シニアエンジニアにとって、Wordは「JSONインターフェースを持つ構造化ドキュメントエンジン」でなければならない。

今回解説した段落書式のシリアライズ技術をマスターすれば、レガシーなVBAマクロとモダンなクラウドインフラストラクチャをシームレスに繋ぐことが可能になる。設計の美しさと実行速度の限界を追求し、真に堅牢な自動化システムを構築してほしい。

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