VBAでREST API連携とJSON処理

Tech

本記事はGeminiの出力をプロンプト工学で整理した業務ドラフト(未検証)です。

VBAでREST API連携とJSON処理

背景と要件

Microsoft Office製品(Excel, Accessなど)におけるVBAは、長らくビジネスプロセス自動化の中核を担ってきました。現代のシステム連携において、REST APIとJSONはデファクトスタンダードとなっており、VBAアプリケーションが外部のWebサービスやクラウドサービスと連携するニーズはますます高まっています。しかし、多くの企業環境ではセキュリティや保守性の観点から、VBAに外部DLLやActiveXコントロールを追加することが厳しく制限される場合があります。 、このような制約下でVBAを用いてREST API連携とJSON処理を実現するための実践的な方法論を解説します。具体的には、外部ライブラリに一切依存せず、Windows標準で提供されるCOMオブジェクト(MSXML2.XMLHTTPWinHttpRequest)と、VBAの組み込み機能(Scripting.DictionaryCollection、文字列操作、正規表現)のみを用いて、HTTPリクエストの送信、JSONデータの送受信、そしてその解析を行う手法に焦点を当てます。また、ExcelやAccessでの利用を想定し、実務レベルで再現可能なコード例と、性能チューニングの重要性についても述べます。

設計

全体アーキテクチャ

VBAアプリケーションがREST APIと連携する際の基本的なアーキテクチャは、以下の要素で構成されます。

  1. HTTPクライアント: WebサーバーへのHTTPリクエストを送信し、レスポンスを受信する役割を担います。VBAではMSXML2.XMLHTTPまたはWinHttpRequestオブジェクトを利用します。

  2. JSONデータ生成/解析: 受信したJSON文字列をVBAのデータ構造(Scripting.DictionaryCollection)に変換したり、VBAのデータをJSON文字列に変換したりする部分です。外部ライブラリを使用しない場合、この部分は手動の文字列操作や専用のクラスモジュールで実装します。

  3. データ処理: JSONから抽出したデータをExcelシートに書き込んだり、Accessのテーブルに挿入したりする処理です。

処理フロー(Mermaid)

API連携の一般的な処理フローを以下に示します。

graph TD
    A["VBAアプリケーション"] --> B{"APIリクエストの準備"};
    B --> C{"HTTPクライアント選択"};
    C -- MSXML2.XMLHTTP60| --> D["HTTPリクエスト送信"];
    C -- WinHttpRequest| --> D;
    D --> E{"HTTPレスポンス受信"};
    E -- 成功 (Status 2xx)| --> F["JSON文字列取得"];
    F --> G{"JSON解析"};
    G --> H["VBAデータ構造への変換"];
    H --> I["ビジネスロジック (Excel/Accessでのデータ処理)"];
    E -- 失敗 (Status 4xx/5xx)| --> J["エラーハンドリング"];
    J --> K["処理終了"];
    I --> K;

HTTPクライアントの選択

VBAでHTTP通信を行う主要なCOMオブジェクトは以下の2つです。

  • MSXML2.XMLHTTP (または MSXML2.XMLHTTP60):

    • 古くから利用されており、広く互換性があります。

    • Microsoft XML, vX.0 参照設定が必要です。

    • 同期/非同期通信の両方に対応。

    • 基本的なGET/POSTリクエストには十分です。

    • Microsoft社のドキュメントでは2023年10月27日時点でも詳細が提供されています[1]。

  • WinHttpRequest:

    • MSXML2.XMLHTTPよりも新しく、一部でより高度な機能(プロキシ設定の柔軟性、SSL/TLS設定など)を提供します。

    • Microsoft WinHTTP Services, version X.0 参照設定が必要です。

    • Microsoft社のドキュメントでは2023年9月18日時点でも詳細が提供されています[2]。

どちらも基本的なAPI連携には使用可能ですが、より新しいWindows環境でセキュリティやプロキシに関する要件がある場合はWinHttpRequestが推奨されることがあります。本記事では両方の使用例を示します。

JSON処理の基本構造

外部ライブラリが使用できない場合、JSONの解析は以下の方法を組み合わせます。

  1. Scripting.DictionaryCollection: JSONオブジェクトはDictionary、JSON配列はCollectionに対応させます。これにより、VBA内部でJSONの階層構造を表現できます。

  2. 文字列操作: InStr, Mid, Replace, Splitなどを駆使して、JSON文字列からキーと値を抽出します。

  3. 正規表現: VBScript.RegExpオブジェクトを利用して、複雑なパターンマッチングを行い、効率的にキーと値を特定します。

完全なJSONパーサーを自作するのは非常に複雑なため、実務では必要なデータ項目のみを抽出する簡易パーサーを実装するか、またはオープンソースのVBA向けJSONパーサークラス(例: VBA-JSONプロジェクト[3])のロジックを参考に、内部利用に限定して移植・改変する方法が考えられます。本記事では、簡易的なJSON文字列からのデータ抽出例を示し、より高度な対応については概念的な説明に留めます。

実装

以下のコードは、Excel VBAを想定していますが、Access VBAでも同様に動作します。

参照設定

VBAエディタ (Alt + F11) を開き、ツール -> 参照設定 から以下のライブラリにチェックを入れてください。

  • Microsoft XML, v6.0 (または v3.0) – MSXML2.XMLHTTP

  • Microsoft WinHTTP Services, version 5.1WinHttpRequest

  • Microsoft Scripting RuntimeScripting.Dictionary

コード1: GETリクエストとJSONレスポンスの基本処理(MSXML2.XMLHTTP60)

ここでは、公開されているダミーAPI(例: https://jsonplaceholder.typicode.com/todos/1)からJSONデータを取得し、Excelシートに表示する例を示します。

' /////////////////////////////////////////////////////////////
' // モジュール名: modApiHelper
' // 目的: REST APIからJSONデータを取得し、Excelシートに表示
' // 適用: Excel VBA (参照設定: Microsoft XML, v6.0; Microsoft Scripting Runtime)
' /////////////////////////////////////////////////////////////

Option Explicit

' 簡易JSONパーサーの定義 (Scripting.Dictionaryを使用)
' JSONオブジェクトを表現する型
Private Type JSONObject
    KeyValues As Scripting.Dictionary
End Type

' JSON配列を表現する型 (Collectionとして利用)
' Private Type JSONArray
'    Items As Collection
' End Type

' JSON文字列から特定のキーの値を抽出する簡易関数
' この関数はネストされていない単純なJSONのみを想定しています。
Function GetJsonValue(ByVal jsonString As String, ByVal key As String) As String
    Const JSON_KEY_QUOTE As String = """"
    Dim pattern As String
    Dim regEx As Object
    Dim matches As Object

    ' JSON文字列から "key": "value" または "key": value (数値など) の形式を抽出
    ' キーがクォートされ、コロンの後に値が続くパターン
    pattern = JSON_KEY_QUOTE & key & JSON_KEY_QUOTE & "\s*:\s*(" & JSON_KEY_QUOTE & ".*??" & JSON_KEY_QUOTE & "|[0-9\-\.]+)"

    Set regEx = CreateObject("VBScript.RegExp")
    With regEx
        .Pattern = pattern
        .IgnoreCase = False
        .Global = False ' 最初の一致のみ
    End With

    Set matches = regEx.Execute(jsonString)

    If matches.Count > 0 Then
        ' キャプチャグループ1が値
        GetJsonValue = Replace(matches(0).SubMatches(0), JSON_KEY_QUOTE, "")
    Else
        GetJsonValue = ""
    End If
End Function

Sub FetchAndParseJson_MSXML2()
    ' パフォーマンス最適化
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    Dim xmlHttp As MSXML2.XMLHTTP60 ' 早期バインディング (参照設定が必要)
    Dim apiUrl As String
    Dim jsonResponse As String
    Dim targetSheet As Worksheet
    Dim lastRow As Long
    Dim parsedTitle As String
    Dim parsedCompleted As String
    Dim startTime As Double, endTime As Double

    startTime = Timer

    Set xmlHttp = New MSXML2.XMLHTTP60
    apiUrl = "https://jsonplaceholder.typicode.com/todos/1" ' ダミーAPI

    ' リクエスト送信
    With xmlHttp
        .Open "GET", apiUrl, False ' 同期通信
        .setRequestHeader "Content-Type", "application/json; charset=UTF-8"
        .send
    End With

    ' レスポンス処理
    If xmlHttp.Status = 200 Then ' HTTP OK
        jsonResponse = xmlHttp.responseText ' JSON文字列を取得
        Debug.Print "Received JSON: " & jsonResponse

        ' 簡易JSON解析
        parsedTitle = GetJsonValue(jsonResponse, "title")
        parsedCompleted = GetJsonValue(jsonResponse, "completed")

        ' Excelシートへの書き出し
        Set targetSheet = ThisWorkbook.Sheets("Sheet1") ' または任意のシート名
        With targetSheet
            .Cells.ClearContents ' 内容をクリア
            .Cells(1, 1).Value = "項目"
            .Cells(1, 2).Value = "値"
            .Cells(2, 1).Value = "API URL"
            .Cells(2, 2).Value = apiUrl
            .Cells(3, 1).Value = "Status Code"
            .Cells(3, 2).Value = xmlHttp.Status
            .Cells(4, 1).Value = "Title"
            .Cells(4, 2).Value = parsedTitle
            .Cells(5, 1).Value = "Completed"
            .Cells(5, 2).Value = parsedCompleted
        End With

        MsgBox "APIデータがSheet1に書き込まれました。", vbInformation
    Else
        MsgBox "APIリクエストに失敗しました。ステータスコード: " & xmlHttp.Status & vbCrLf & "レスポンステキスト: " & xmlHttp.responseText, vbCritical
    End If

    ' リソースの解放
    Set xmlHttp = Nothing

    endTime = Timer
    Debug.Print "処理時間: " & (endTime - startTime) & "秒"

    ' パフォーマンス設定を元に戻す
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
End Sub

コードのポイント:

  • MSXML2.XMLHTTP60: HTTPリクエストの送受信に利用します。Openメソッドでリクエストの種類、URL、同期/非同期を指定。setRequestHeaderでヘッダーを追加し、sendでリクエストを送信します。

  • GetJsonValue関数: 外部ライブラリを使わずにJSONから特定のキーの値を抽出する簡易的な関数です。正規表現(VBScript.RegExp)を使用しています。ネストされたJSONや配列には対応していません。

  • エラーハンドリング: xmlHttp.StatusでHTTPステータスコードを確認し、成功(200番台)か失敗かを判断します。

  • 性能チューニング: Application.ScreenUpdating = FalseApplication.Calculation = xlCalculationManualで画面更新と自動計算を一時停止し、処理速度を向上させています。

コード2: POSTリクエストとJSONデータ送信(WinHttpRequest)

ここでは、ダミーAPIエンドポイントに対してJSONデータをPOSTリクエストとして送信する例を示します。

' /////////////////////////////////////////////////////////////
' // モジュール名: modApiHelper
' // 目的: REST APIにJSONデータをPOST送信
' // 適用: Excel VBA (参照設定: Microsoft WinHTTP Services, version 5.1)
' /////////////////////////////////////////////////////////////

Option Explicit

' JSON文字列をエスケープする簡易関数
' この関数は、クォーテーションや改行などの特殊文字をエスケープします。
Function EscapeJsonString(ByVal inputString As String) As String
    ' 改行、タブなどのエスケープはここでは省略し、主にダブルクォーテーションをエスケープ
    EscapeJsonString = Replace(inputString, """", "\""")
    ' 必要に応じてその他のエスケープ処理を追加:
    ' EscapeJsonString = Replace(EscapeJsonString, vbCrLf, "\n")
    ' EscapeJsonString = Replace(EscapeJsonString, vbCr, "\r")
    ' EscapeJsonString = Replace(EscapeJsonString, vbTab, "\t")
    ' EscapeJsonString = Replace(EscapeJsonString, "\", "\\") ' バックスラッシュもエスケープが必要な場合
End Function

Sub PostJsonData_WinHttpRequest()
    ' パフォーマンス最適化
    Application.ScreenUpdating = False
    Application.Calculation = xlCalculationManual

    Dim winHttpReq As WinHttpRequest ' 早期バインディング (参照設定が必要)
    Dim apiUrl As String
    Dim jsonPayload As String
    Dim startTime As Double, endTime As Double

    startTime = Timer

    Set winHttpReq = New WinHttpRequest
    apiUrl = "https://jsonplaceholder.typicode.com/posts" ' ダミーAPI (POST用)

    ' 送信するJSONデータを構築 (手動で文字列結合)
    ' 実際のデータはExcelシートやAccessテーブルから取得する
    Dim userId As Long: userId = 1
    Dim title As String: title = "VBAでAPI連携テスト"
    Dim bodyContent As String: bodyContent = "これはVBAから送信されたテストデータです。"

    jsonPayload = "{" & _
                  """userId"": " & userId & "," & _
                  """title"": """ & EscapeJsonString(title) & """," & _
                  """body"": """ & EscapeJsonString(bodyContent) & """" & _
                  "}"

    Debug.Print "Sending JSON: " & jsonPayload

    ' リクエスト送信
    With winHttpReq
        .Open "POST", apiUrl, False ' 同期通信
        .setRequestHeader "Content-Type", "application/json; charset=UTF-8"
        ' タイムアウト設定 (ミリ秒単位)
        .SetTimeouts ResolveTimeout:=5000, ConnectTimeout:=5000, SendTimeout:=5000, ReceiveTimeout:=10000
        .send jsonPayload
    End With

    ' レスポンス処理
    If winHttpReq.Status >= 200 And winHttpReq.Status < 300 Then ' 成功 (2xx台)
        MsgBox "APIにJSONデータをPOSTしました。ステータスコード: " & winHttpReq.Status & vbCrLf & "レスポンステキスト: " & winHttpReq.ResponseText, vbInformation
    Else
        MsgBox "APIリクエストに失敗しました。ステータスコード: " & winHttpReq.Status & vbCrLf & "レスポンステキスト: " & winHttpReq.ResponseText, vbCritical
    End If

    ' リソースの解放
    Set winHttpReq = Nothing

    endTime = Timer
    Debug.Print "処理時間: " & (endTime - startTime) & "秒"

    ' パフォーマンス設定を元に戻す
    Application.Calculation = xlCalculationAutomatic
    Application.ScreenUpdating = True
End Sub

コードのポイント:

  • WinHttpRequest: MSXML2.XMLHTTPと同様にHTTPリクエストを送信しますが、SetTimeoutsなどより詳細な設定が可能です。

  • JSONペイロードの構築: VBAではJSON文字列を手動で構築する必要があります。EscapeJsonString関数は、ダブルクォーテーションをエスケープする簡易的な例です。複雑なデータ構造の場合は、Scripting.DictionaryCollectionを使ってVBA内部でデータを構築し、それを再帰的にJSON文字列に変換する自作関数が必要になります。

  • エラーハンドリング: winHttpReq.Statusが200番台であるかを確認し、成功・失敗を判断します。

Win32 APIについて

本稿の範囲では、MSXML2.XMLHTTPWinHttpRequestといった既存のCOMオブジェクトとScripting.Runtimeオブジェクトで必要な機能がカバーできるため、Declare PtrSafeを使ったWin32 APIの直接呼び出しは行いません。これらは主に、VBA標準では提供されない低レベルなシステム操作(例: ファイルシステムの特定の操作、COMオブジェクトにないOS機能の利用)が必要な場合に検討されます。

検証

  1. GETリクエストの検証:

    • FetchAndParseJson_MSXML2を実行し、Sheet1にAPIからのデータ(タイトル、完了ステータスなど)が正確に書き込まれていることを確認します。

    • Debug.Printウィンドウで受信したJSON文字列と処理時間を確認します。

    • インターネット接続がない場合やAPIがダウンしている場合に、エラーメッセージが適切に表示されることを確認します。

  2. POSTリクエストの検証:

    • PostJsonData_WinHttpRequestを実行し、成功メッセージと返却されるレスポンス(通常、ダミーAPIは送信したデータを返す)を確認します。

    • エラー発生時(例: apiUrlを存在しないものに変更)に、適切なエラーメッセージが表示されることを確認します。

運用

APIキーの管理

APIキーなどの機密情報は、コード内に直接記述せず、以下のいずれかの方法で管理します。

  • Excel/Accessの非表示シート: パスワード保護された非表示シートに格納し、実行時にVBAで読み込む。

  • 環境変数: Windowsの環境変数に設定し、Environ関数で取得する。

  • レジストリ: レジストリに格納し、Win32 API (RegOpenKeyEx, RegQueryValueExなど) で読み込む。

エラーロギング

API呼び出しが失敗した場合や、JSON解析で予期せぬエラーが発生した場合は、その詳細(日時、API URL、ステータスコード、エラーメッセージ)をログファイル(テキストファイル)やExcelシート、Accessテーブルに記録することで、問題の特定とデバッグを容易にします。

API利用制限(レートリミット)への配慮

多くの商用APIには利用回数制限(レートリミット)があります。VBAで大量のAPIコールを行う場合は、以下の対策が必要です。

  • 待機処理: API呼び出し間にApplication.WaitSleep (Declare PtrSafe Sub Sleep Lib "kernel32" (ByVal dwMilliseconds As Long)) を使って意図的に遅延を設ける。

  • 再試行ロジック: レートリミットエラー(通常HTTP 429 Too Many Requests)を受け取った場合に、指数バックオフなどのアルゴリズムでリクエストを再試行する。

性能チューニング

VBAでAPI連携を行う際、API呼び出しそのものの速度はネットワークI/Oに依存しますが、VBA側の処理を最適化することで体感速度や全体の実行時間を大幅に改善できます。

  1. Application.ScreenUpdating = False:

    • Excelシートの描画更新を一時的に停止します。特に大量のデータをシートに書き込む場合に絶大な効果を発揮します。

    • 効果: 1000行のデータ書き込みで、数秒かかっていた処理が0.1秒以下になるケースもあります。

  2. Application.Calculation = xlCalculationManual:

    • Excelの自動計算を停止します。数式が多く含まれるシートにデータを書き込む際に効果的です。

    • 効果: 数百から数千の計算式を含むシートで、自動計算を停止することで処理時間が数倍から数十倍改善されることがあります。

  3. 配列による一括処理:

    • Excelシートへのセルの書き込みは、1セルずつ行うと非常に遅くなります。データを一度VBAの配列に格納し、最後にRange.Value = Arrayで一括してシートに書き込むことで高速化できます。

    • 効果: 1万セルの書き込みで、ループ処理が10秒以上かかるのに対し、配列一括書き込みは0.1秒以下で完了する場合があります。

  4. オブジェクト参照の早期バインディング:

    • Dim xmlHttp As MSXML2.XMLHTTP60 のように、参照設定を行い明示的に型を宣言する(早期バインディング)ことで、実行時のオブジェクト解決が高速になります。

    • Dim xmlHttp As Object (遅延バインディング) に比べて、わずかではありますがパフォーマンスが向上します。

上記のチューニングは、本記事のコード例にも組み込んでいます。

落とし穴

  1. 文字エンコーディング:

    • APIレスポンスがUTF-8であるにも関わらず、VBAで適切に処理しないと文字化けが発生します。MSXML2.XMLHTTPWinHttpRequestは通常UTF-8を正しく処理しますが、JSONパーシング時に問題になることがあります。
  2. SSL/TLSバージョン問題:

    • 古いWindows環境やVBAバージョンでは、APIが要求するTLS 1.2以降に対応できず、SSL接続エラーが発生することがあります。OSのアップデートや、WinHttpRequestSetClientCertificateなどの詳細設定を検討する必要があります。
  3. JSONの複雑性:

    • ネストされたオブジェクトや配列が深く、かつ項目数が多いJSONの解析は、手動での文字列操作だけでは非常に困難になります。前述のScripting.DictionaryCollectionを用いた再帰的なパーサーの自作が必要になりますが、これは高度なプログラミングスキルと時間が必要です。
  4. エラーハンドリングの不備:

    • ネットワークエラー、API側のエラー(4xx/5xx系ステータスコード)、JSONフォーマットエラーなど、様々な失敗ケースを想定した堅牢なエラーハンドリングが必要です。
  5. 認証方式:

    • Basic認証、APIキー(ヘッダー/クエリパラメータ)、OAuth2.0など、APIによって認証方式が異なります。Basic認証やAPIキーは比較的容易に対応できますが、OAuth2.0はトークン取得フローが複雑で、VBAで完全に実装するのは困難な場合があります。

まとめ

VBAは、外部ライブラリの制約がある環境下でも、Windows標準のCOMオブジェクト(MSXML2.XMLHTTPWinHttpRequest)を活用することでREST APIとの連携が可能です。また、Scripting.DictionaryCollection、そして文字列操作を駆使すればJSONデータの送受信と簡易的な解析も実現できます。

実務においては、単なる機能の実装だけでなく、APIキーのセキュアな管理、堅牢なエラーハンドリング、そしてApplication.ScreenUpdatingや配列による一括書き込みといった性能チューニングが、安定したVBAアプリケーションの運用には不可欠です。複雑なJSON構造に対応する場合は、専用のVBAクラスモジュールを自作することを検討し、将来的なメンテナンス性も考慮した設計が重要となります。

実行手順とロールバック方法

実行手順

  1. ExcelまたはAccessを開く: 新規ブック/データベースを作成するか、既存のものを開きます。

  2. VBAエディタの起動: Alt + F11キーを押してVBAエディタを起動します。

  3. モジュールの挿入: 挿入メニューから標準モジュールを選択し、新しいモジュールを挿入します。

  4. 参照設定:

    • ツールメニュー -> 参照設定を選択します。

    • リストから「Microsoft XML, v6.0」(またはv3.0)、「Microsoft WinHTTP Services, version 5.1」、「Microsoft Scripting Runtime」を見つけてチェックを入れ、「OK」をクリックします。

  5. コードの貼り付け: 本記事の「コード1: GETリクエストとJSONレスポンスの基本処理」および「コード2: POSTリクエストとJSONデータ送信」のVBAコードを、それぞれ新しい標準モジュールにコピー&ペーストします。

  6. シートの準備 (Excelの場合):

    • Excelの場合、Sheet1という名前のシートがあることを確認します。もし存在しない場合は、新しいシートを作成して名前をSheet1に変更してください。このシートにAPIレスポンスが書き込まれます。
  7. マクロの実行:

    • VBAエディタでいずれかのSubプロシージャ(例: FetchAndParseJson_MSXML2)内にカーソルを置き、F5キーを押すか、実行メニューからSub/ユーザーフォームの実行を選択します。

    • Excelシートまたはメッセージボックスに結果が表示されます。

  8. 動作確認: 想定通りのデータが表示されるか、エラーハンドリングが機能するかを確認します。

ロールバック方法

  1. VBAモジュールの削除:

    • VBAエディタのプロジェクトエクスプローラー(通常左側ペイン)で、挿入した標準モジュール(例: Module1)を右クリックし、「Module1の削除」を選択します。

    • 「エクスポートしますか?」と聞かれたら、「いいえ」を選択して削除を確定します。

  2. 参照設定の解除:

    • ツールメニュー -> 参照設定を選択します。

    • 先ほどチェックを入れた「Microsoft XML, v6.0」、「Microsoft WinHTTP Services, version 5.1」、「Microsoft Scripting Runtime」のチェックを外し、「OK」をクリックします。

  3. Excelシート/Accessテーブルの変更の取り消し:

    • Excelの場合、コード実行によってSheet1に書き込まれたデータは手動でクリアまたは元に戻します。

    • Accessの場合、もしデータがテーブルに挿入されたのであれば、該当レコードを削除します。

  4. ファイルの保存なし:

    • もしテスト用に新規作成したブック/データベースであれば、変更を保存せずにファイルを閉じます。

これらの手順により、システムへの影響を最小限に抑えながらVBAプロジェクトを元の状態に戻すことができます。


参考文献 [1] Microsoft. (2023年10月27日). XMLHttpRequest Object. https://learn.microsoft.com/ja-jp/windows/win32/msxml/xmlhttprequest [2] Microsoft. (2023年9月18日). About WinHTTP. https://learn.microsoft.com/ja-jp/windows/win32/winhttp/about-winhttp [3] VBA-tools. (2024年6月15日). VBA-JSON. https://github.com/VBA-tools/VBA-JSON (本記事では外部ライブラリとして直接使用せず、概念的参考に留める) [4] Microsoft. (2024年4月10日). Excel VBA のパフォーマンスに関するヒント. https://learn.microsoft.com/ja-jp/office/vba/excel/concepts/working-with-excel/excel-vba-performance-tips

ライセンス:本記事のテキスト/コードは特記なき限り CC BY 4.0 です。引用の際は出典URL(本ページ)を明記してください。
利用ポリシー もご参照ください。

コメント

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