Wordの表をJSONにシリアライズし、Web API経由でSQL Serverへ直接保存する連携術
Word VBAを単なる「お蔵入りした定型文書の自動生成ツール」と捉えているうちは、真のレガシーの呪縛から逃れることはできない。Wordのドキュメント、とりわけ`Table`オブジェクト構造体は、人間が視覚的に構造化データを入力するための極めて強力なUIフロントエンドである。
現場の業務担当者がExcelや専用フォームではなく「Wordの表」にデータを入力し、その場でマクロを実行するだけで、背後のWeb APIを叩いてクラウドやオンプレミスのSQL Serverへトランザクションを直接流し込む――。
このアーキテクチャを構築できれば、ファイルサーバーの肥大化やバージョン管理の破綻という、エンタープライズ特有の悪夢から解放される。
今回は、Wordの表(Table)をゼロ依存でJSONへとシリアライズし、`MSXML2.ServerXMLHTTP.6.0`を用いて非同期通信(あるいは同期・堅牢な同期通信)でバックエンドのREST APIへ直接送出する、シニアエンジニア必携の極限の知見を公開する。
—
1. アーキテクチャの全体像と設計思想
本システム連携における最大の障壁は、Word VBAの標準環境にはJSONパーサーや強力なHTTPクライアントが存在しないという点だ。外部ライブラリ(参照設定)の追加は、配布時のDLL地獄(DLL Hell)を招くため、エンタープライズ環境ではご法度とされることが多い。
したがって、以下の設計原則を貫く。
1. 完全な遅延バインディングとゼロ外部依存:
参照設定を一切行わず、Windows標準コンポーネントのみで完結させる。
2. メモリの厳格なライフサイクル管理:
Word VBAのCOMオブジェクトはガベージコレクションが極めてルーズである。明示的なインスタンス解放(`Set obj = Nothing`)を怠れば、連続実行時に必ずメモリリークやWordプロセスのハングアップを引き起こす。
3. 表構造の正規化:
Wordの表特有の「セル結合(Merge)」や「ネストされた表」によるパース崩壊を防ぐため、走査アルゴリズムを厳密に定義する。
—
2. 実装コード:Word表のJSON変換とAPI送信エンジン
以下のモジュールは、アクティブドキュメントの最初の表を走査し、2行目以降(1行目はヘッダーと仮定)をJSONオブジェクトの配列に変換した上で、指定されたエンドポイントへHTTPS POSTリクエストを送信する。
Option Explicit
‘ ==============================================================================
‘ 処理名: ExportTableToSQLServerAPI
‘ 概要: Word文書内の指定テーブルをJSON化し、Web API経由でDBへ保存する
‘ ==============================================================================
Public Sub ExportTableToSQLServerAPI()
Dim targetTable As Table
Dim jsonString As String
Dim apiEndpoint As String
‘ 接続先Web APIのエンドポイント(環境に合わせて変更)
apiEndpoint = “https://api.your-enterprise.local/v1/documents/sync”
‘ 1. 対象テーブルの取得(ここではドキュメントの最初の表とする)
If ActiveDocument.Tables.Count = 0 Then
MsgBox “ドキュメント内にテーブルが存在しません。”, vbCritical, “システムエラー”
Exit Sub
End If
Set targetTable = ActiveDocument.Tables(1)
‘ 2. 表の構造をJSON文字列へシリアライズ
jsonString = SerializeTableToJson(targetTable)
If Len(jsonString) = 0 Then
MsgBox “シリアライズ可能なデータが見つかりませんでした。”, vbExclamation, “警告”
Exit Sub
End If
‘ 3. Web APIへの送信実行
Call SendJsonViaServerXMLHTTP(apiEndpoint, jsonString)
End Sub
‘ ==============================================================================
‘ 内部関数: テーブルのJSONシリアライザー(結合セル・制御文字対策済み)
‘ ==============================================================================
Private Function SerializeTableToJson(ByVal tbl As Table) As String
Dim r As Long, c As Long
Dim rowCount As Long, colCount As Long
Dim headers() As String
Dim cellText As String
Dim sb As String
Dim rowJson As String
Dim isFirstRow As Boolean
Dim isFirstItem As Boolean
rowCount = tbl.Rows.Count
colCount = tbl.Columns.Count
If rowCount < 2 Then SerializeTableToJson = "" Exit Function End If ' ヘッダー行(1行目)の取得 ReDim headers(1 To colCount) For c = 1 To colCount headers(c) = CleanJsonString(GetCellText(tbl, 1, c)) Next c ' JSON配列の構築開始 sb = "[" & vbCrLf isFirstRow = True ' 2行目からデータ行として走査 For r = 2 To rowCount rowJson = "" isFirstItem = True For c = 1 To colCount cellText = CleanJsonString(GetCellText(tbl, r, c)) If Not isFirstItem Then rowJson = rowJson & "," & vbCrLf End If ' キーと値のペアを作成(数値の場合はクォートを外す判定を入れても良い) rowJson = rowJson & " """ & headers(c) & """: """ & cellText & """" isFirstItem = False Next c If Not isFirstRow Then sb = sb & "," & vbCrLf End If sb = sb & " {" & vbCrLf & rowJson & vbCrLf & " }" isFirstRow = False Next r sb = sb & vbCrLf & "]" SerializeTableToJson = sb End Function ' ============================================================================== ' ヘルパー関数: セルテキストの安全な取得(結合セルの安全網) ' ============================================================================== Private Function GetCellText(ByVal tbl As Table, ByVal rowIdx As Long, ByVal colIdx As Long) As String On Error Resume Next Dim txt As String ' Wordのセル終端文字(Chr(13) + Chr(7))を除去 txt = tbl.Cell(rowIdx, colIdx).Range.Text txt = Left(txt, Len(txt) - 2) GetCellText = txt On Error GoTo 0 End Function ' ============================================================================== ' ヘルパー関数: JSONインジェクション・制御文字のエスケープ処理 ' ============================================================================== Private Function CleanJsonString(ByVal src As String) As String Dim dst As String dst = src dst = Replace(dst, "\", "\\") dst = Replace(dst, """", "\""") dst = Replace(dst, vbCrLf, "\n") dst = Replace(dst, vbCr, "\n") dst = Replace(dst, vbLf, "\n") dst = Replace(dst, vbTab, "\t") CleanJsonString = dst End Function ' ============================================================================== ' 通信モジュール: MSXML2.ServerXMLHTTP.6.0 による堅牢なAPI送信 ' ============================================================================== Private Sub SendJsonViaServerXMLHTTP(ByVal url As String, ByVal jsonPayload As String) Dim httpObj As Object Dim status As Long Dim responseText As String ' 遅延バインディングによるインスタンス生成(レガシー環境配慮) Set httpObj = CreateObject("MSXML2.ServerXMLHTTP.6.0") On Error GoTo ErrorHandler With httpObj ' タイムアウト設定 (ミリ秒): 接続10秒、送信10秒、受信30秒 .setTimeouts 10000, 10000, 30000, 30000 .Open "POST", url, False ' 同期通信(非同期にする場合はTrueだが、VBAのライフサイクル上同期が安全) .setRequestHeader "Content-Type", "application/json; charset=utf-8" .setRequestHeader "X-Client-Origin", "Word-VBA-Enterprise" ' 認証トークンが必要な場合はここで付与 ' .setRequestHeader "Authorization", "Bearer YOUR_API_TOKEN_HERE" .send jsonPayload status = .Status responseText = .responseText End With ' 終了処理 Set httpObj = Nothing If status >= 200 And status < 300 Then MsgBox "SQL Serverへのデータ保存が正常に完了しました。", vbInformation, "通信成功" Else MsgBox "サーバー側でエラーが発生しました。" & vbCrLf & _ "ステータスコード: " & status & vbCrLf & _ "レスポンス: " & responseText, vbCritical, "APIエラー" End If Exit Sub ErrorHandler: ' 異常終了時のメモリリーク防止 If Not httpObj Is Nothing Then Set httpObj = Nothing MsgBox "通信中に致命的なエラーが発生しました。" & vbCrLf & _ "エラー番号: " & Err.Number & vbCrLf & _ "内容: " & Err.Description, vbCritical, "致命的エラー" End Sub ---
3. チーフアーキテクトが解説するコードの急所
① Word表の「セル終端文字」という罠
Wordの`Cell.Range.Text`は、純粋な文字列だけでなく、セル自体の終端を表す制御文字(`Chr(13) & Chr(7)`)を末尾に含んでいる。これをそのままJSONに含めると、SQL Server側のパース処理で構文エラー(JSON Parse Error)を引き起こすか、データベースにゴミデータが混入する。
`GetCellText`関数内で行っている `Left(txt, Len(txt) – 2)` による切り捨て処理は、Word VBAで表データを扱う際の絶対的な鉄則である。
② メモリリークを根絶する例外設計
VBAにおける `CreateObject` は非常に強力だが、通信中にタイムアウトやネットワーク切断などの例外(Error)が発生した際、適切にオブジェクトを参照解除(`Set httpObj = Nothing`)しないと、Wordプロセス内にCOMラッパーの残骸が残り続け、やがてWord本体が強制終了する原因になる。
上記のコードでは、`ErrorHandler` ラベルを確実に通過させ、例外時であっても確実にメモリを解放する堅牢な構造にしている。
③ バックエンド(SQL Server側)へのアプローチ
受け手となるWeb API(ASP.NET CoreやNode.js、Python FastAPI等)側では、受け取ったJSON配列をバルクインサート(一括挿入)あるいはストアドプロシージャに渡すことで、高速かつ安全にSQL Serverへ書き込むことができる。
— 【参考】SQL Server側で受け取る際のストアドプロシージャのイメージ
CREATE PROCEDURE dbo.sp_ImportWordTableData
@JsonData NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
INSERT INTO TargetTable (Col1, Col2, CreatedAt)
SELECT
JSON_VALUE(p.value, ‘$.列名1’),
JSON_VALUE(p.value, ‘$.列名2’),
GETDATE()
FROM OPENJSON(@JsonData) AS p;
END
—
4. おわりに
「Wordから直接データベースへ繋ぐ」という発想は、一見するとレガシーなVBAの枠を超えた異端の技法に見えるかもしれない。しかし、現場のユーザーにとって最も使い慣れたインターフェース(Word)を殺さず、モダンなWeb APIエコシステムとシームレスに結合させるこのアプローチは、リプレイス費用をかけずにレガシーシステムを延命・近代化させるための極めて高費用対効果なソリューションである。
オブジェクトの挙動を熟知し、メモリの隅々までコントロール下に置いたコードだけが、企業のミッションクリティカルな現場で長期間沈黙を守りながら稼働し続けることができる。プロフェッショナルとして、妥協のない実装を心がけてほしい。
