【実務・中級編】ADOのRecordsetをJSON形式に変換する:Web API連携を見据えたデータ構造の変換術 – Access VBA解析バイブル

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

「文字列連結」という素人仕事から脱却せよ:ADO RecordsetをJSONへと昇華させる、堅牢なシリアライズ設計術

Access VBAを単なる「社内用データベース」として閉じ込めているうちは、エンジニアとしての成長は止まったも同然だ。現代の業務自動化において、Accessは「データのエンジン」であり、その出力先はWeb API、クラウドストレージ、あるいはマイクロサービスへと広がっている。

そこで直面する最大の壁が、「Recordset(レコードセット)というリレーショナルな構造を、いかにしてJSONという階層的なデータ構造へ、バグなく、高速に変換するか」という問題だ。

多くの開発者が陥る罠がある。それは、`json = json & “{“”id””:” & rs!id & “}”` といった、単純な文字列連結によるシリアライズだ。これは設計として極めて脆弱であり、データの中にダブルクォートや改行が含まれた瞬間に、生成されるJSONは破壊され、API連携はエラーを吐いて沈黙する。

本稿では、プロフェッショナルとして、どのようなデータが来ても決して壊れない、堅牢な「Recordset to JSON」のアーキテクチャを伝授する。

1. なぜDAOではなく、ADOを選択すべきなのか

議論の前に、アーキテクチャの前提を整理しておく。Access内部のテーブル操作であればDAO(Data Access Objects)で十分だ。しかし、Web API連携を見据えるなら、ADO(ActiveX Data Objects)を選択するのが定石である。

理由は、ADOが「データソースに依存しない、より抽象化されたデータモデル」を提供しているからだ。ADOのRecordsetは、カーソル制御やDisconnected Recordset(メモリ上にデータを保持する概念)の扱いにおいて、外部システムとのやり取りを意識した設計になっている。Web APIへ送るための「ペイロード(データ本体)」を構築するプロセスにおいて、ADOのフィールドオブジェクトが持つ型情報へのアクセスは、シリアライザを構築する上で極めて強力な武器となる。

2. 破綻する設計、堅牢な設計:その境界線

現場でよく見る「動いているが、いつか必ず壊れるコード」と、私が推奨する「プロダクションレベルのコード」の違いを理解してほしい。

非効率かつ脆弱な設計(Anti-Pattern)

  • 型変換の無視: 数値、文字列、日付、Nullの区別をせず、すべてを文字列として結合している。
  • エスケープ処理の欠如: データ内の `”` (ダブルクォート) や `\` (バックスラッシュ) を考慮していない。
  • メモリ管理の放棄: ループ内で巨大な文字列を連結し続け、メモリ再割り当てのオーバーヘッドを無視している。

プロフェッショナルな設計(Best Practice)

  • 型に応じた適切なシリアライズ: `Null` は `null` へ、文字列は `”` で囲み、日付は ISO 8601 形式へ。
  • 完全なエスケープ処理: 特殊文字をJSON規格に従って適切に置換する。
  • 動的なスキーマ対応: Recordsetのフィールド構成が変わっても、コードを書き換えずに対応できる。

3. 実装:Recordset to JSON シリアライザ

以下に、そのままプロジェクトに組み込めるレベルの、堅牢なシリアライザの実装例を示す。このコードは、Recordsetのフィールド構成を動的に解析し、型に基づいた正しいJSON配列を生成する。

※事前に「Microsoft ActiveX Data Objects x.x Library」への参照設定が必要である。

Option Explicit

''
' ======================================================================================
' Module: modJsonSerializer
' Description: ADO RecordsetをJSON文字列へ変換する、堅牢なシリアライザ
' Author: Chief Architect
' ======================================================================================
''

''' <summary>
''' ADO RecordsetをJSON形式の文字列に変換する。
''' </summary>
''' <param name="rs">変換対象のADO Recordset</param>
''' <returns>JSON形式の文字列。エラー時は空文字を返す。</returns>
Public Function RecordsetToJSON(ByRef rs As ADODB.Recordset) As String
    On Error GoTo ErrorHandler

    ' Recordsetの状態チェック
    If rs Is Nothing Then Exit Function
    If rs.State = adStateClosed Then Exit Function
    If rs.EOF And rs.BOF Then Exit Function

    Dim jsonResult As String
    Dim field As ADODB.Field
    Dim row As Long
    Dim fieldValues() As String
    Dim i As Integer

    ' JSON配列の開始
    jsonResult = "["

    ' レコードのループ
    Do Until rs.EOF
        jsonResult = jsonResult & "{"
        
        ' フィールドのループ(動的なスキーマ対応)
        For i = 0 To rs.Fields.Count - 1
            Set field = rs.Fields(i)
            
            ' キーの追加
            jsonResult = jsonResult & """" & EscapeJSON(field.Name) & """:"
            
            ' 値のシリアライズ(型に応じた分岐)
            jsonResult = jsonResult & SerializeValue(field.Value)
            
            ' カンマ制御
            If i < rs.Fields.Count - 1 Then
                jsonResult = jsonResult & ","
            End If
        Next i
        
        jsonResult = jsonResult & "}"
        
        rs.MoveNext
        If Not rs.EOF Then
            jsonResult = jsonResult & ","
        End If
    Loop

    ' JSON配列の終了
    jsonResult = jsonResult & "]"
    
    RecordsetToJSON = jsonResult
    Exit Function

ErrorHandler:
    ' 実務ではログ出力クラスを呼び出すこと
    Debug.Print "Error in RecordsetToJSON: " & Err.Description
    RecordsetToJSON = ""
End Function

''' <summary>
''' フィールドの値の型に基づき、JSONに適した形式に変換する。
''' </summary>
Private Function SerializeValue(ByVal val As Variant) As String
    ' 1. Nullの処理
    If IsNull(val) Then
        SerializeValue = "null"
        Exit Function
    End If

    ' 2. 型判定
    Select Case VarType(val)
        Case vbString
            ' 文字列:エスケープしてダブルクォートで囲む
            SerializeValue = """" & EscapeJSON(CStr(val)) & """"
            
        Case vbBoolean
            ' 真偽値:true/false
            SerializeValue = IIf(val, "true", "false")
            
        Case vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal
            ' 数値:そのまま(ダブルクォートは不要)
            SerializeValue = CStr(val)
            
        Case vbDate
            ' 日付:ISO 8601形式 (YYYY-MM-DDThh:mm:ss)
            SerializeValue = """" & Format(val, "yyyy-mm-ddThh:mm:ss") & """"
            
        Case Else
            ' 未知の型は文字列として扱う(安全策)
            SerializeValue = """" & EscapeJSON(CStr(val)) & """"
    End Select
End Function

''' <summary>
''' JSON文字列として不正な文字をエスケープする。
''' </summary>
Private Function EscapeJSON(ByVal str As String) As String
    Dim res As String
    res = str
    ' 特殊文字のエスケープ(順序が重要)
    res = Replace(res, "\", "\\")   ' バックスラッシュ
    res = Replace(res, """", "\""") ' ダブルクォート
    res = Replace(res, vbCr, "\r")  ' キャリッジリターン
    res = Replace(res, vbLf, "\n")  ' ラインフィード
    res = Replace(res, vbTab, "\t") ' タブ
    EscapeJSON = res
End Function

4. アーキテクチャの要諦:なぜこのコードは「強い」のか

このコードが、単なる「動くコード」を超えて「プロダクション品質」である理由を解説する。

① 型の厳格なハンドリング

`SerializeValue` 関数に注目してほしい。VBAの `Variant` 型は非常に柔軟だが、JSONの世界では「型」が厳格だ。`Null` を単なる空文字 `””` として送ってしまうと、受け取り側のAPIで「必須項目が欠落している」と判定され、エラーになる。`IsNull` を判定し、明示的に `null` というリテラルを返す。これが、システム間連携における「契約」を守る作法だ。

② 文字列エスケープの徹底

`EscapeJSON` 関数は、このシリアライザの心臓部だ。
例えば、ユーザーの備考欄に `He said, “Hello”` という文字列が入っていた場合、エスケープなしでは `{“memo”: “He said, “Hello””}` となり、JSONとして構文エラーになる。これを `\”` に置換することで、正しい `{“memo”: “He said, \”Hello\””}` を生成する。この一手間が、システムの可用性を決定づける。

③ スキーマ・アグノスティック(スキーマに依存しない)

`rs.Fields.Count` を用いてループを回しているため、SQL文を書き換えて取得フィールドを増やしたり減らしたりしても、この関数自体を修正する必要はない。これは、保守コストを劇的に下げる設計思想である。

5. 実務における注意点:オブジェクトのライフサイクル

最後に、エンジニアとして最も重要な「メモリ管理」について触れておく。

RecordsetをJSONに変換した後、そのRecordsetオブジェクトを放置してはならない。特に、ループ内で大量のRecordsetを生成する場合、`rs.Close` を呼び出し、必ず `Set rs = Nothing` でオブジェクトを解放すること。

また、APIへ送信する際は、生成した巨大なJSON文字列をそのまま `MSXML2.XMLHTTP` 等に渡すことになるが、文字列が数十MBを超えるような場合は、VBAのメモリ限界(String型のサイズ制限)に注意が必要だ。その場合は、Recordsetを分割して処理する「チャンク(塊)処理」の検討が必要になる。

まとめ

Access VBAによる外部連携は、もはや「おまけ」ではない。
「データの型を理解し、エスケープを徹底し、動的な構造に対応する」。
この3点を守るだけで、あなたの作成するツールは、単なる「便利ツール」から、エンタープライズ環境に耐えうる「堅牢なデータエンジン」へと進化する。

さあ、文字列連結の泥沼から抜け出し、設計に基づいたコーディングを開始せよ。

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