【実務・中級編】【上級者向け】WBSの階層構造をJSON形式にシリアライズし、Webダッシュボードと連携するAPI開発 – Project VBA解析バイブル

スポンサーリンク

【上級者向け】MS ProjectのWBSをJSONへ極限効率シリアライズ!Webダッシュボード連携API開発の極意

業務自動化エンジニアの皆さん、こんにちは。
日々のプロジェクトマネジメントにおいて、Microsoft Project(以下、Project)の重厚長大なUIにフラストレーションを感じたことはないか?「経営層や他部署のメンバーに見せるために、もっと軽快なWebガントチャートで共有したい」「自社の独自ダッシュボードとリアルタイムに連携させたい」――そう考えた時、必ず壁となるのが「Projectの複雑なタスク階層(WBS)と依存関係を、いかに美しく、かつ高速にJSONへ変換するか」という問題だ。

ネット上には「Taskオブジェクトをループさせて文字列を結合するだけ」の稚拙なコードが溢れているが、そんなアプローチでは大規模プロジェクトでメモリリークを起こすか、階層の深さ(IndentLevel)が狂って使い物にならなくなる。

今回は、Project VBAのオブジェクトモデルの裏側を知り尽くした私が、タスクの階層構造、前提条件(先行・後続タスク)、制約条件を完全に保持しつつ、超高速にJSONへシリアライズしてWebフロントエンドへ受け渡すプロダクションコードを伝授する。

—

1. なぜ従来のVBA製JSON出力は「使えない」のか?

多くのエンジニアが犯す最大のミスは、VBA上で愚直に文字列連結(`json = json & “…”`)を繰り返すことだ。これには以下の致命的な欠点がある。

  • メモリの断片化とパフォーマンスの劣化: VBAの文字列結合は、長くなればなるほどO(N^2)のオーダーで処理コストが増大する。数千行規模のWBSになると、処理が数分で終わらない事態に陥る。
  • エスケープ処理の漏れ: タスク名にダブルクォーテーション(`”`)や改行コードが含まれていた場合、生成されるJSONが即座にパースエラーを起こす。
  • 階層(WBS)の整合性欠落: 単にインデントレベルを見るだけでは、途中に「非表示タスク」や「サマリータスクの開閉状態」が挟まった際に、親子関係が完全に崩壊する。

これを解決するためには、「VBA内で独自のJSONビルダーを構築し、メモリ上で構造体を安全に組み立ててから一括出力する」というアプローチが不可欠である。

—

2. 堅牢なシリアライズ設計のポイント

今回のアーキテクチャでは、以下の3点を担保する。

1. 再帰的走査(またはスタック制御)によるWBSの正確な親子関係の抽出

  • Projectの `Task.OutlineLevel` と `Task.ChildTasks` を利用し、ルートからリーフまで漏れなくツリー構造化する。

2. 依存関係(Predecessors / Successors)の完全網羅

  • 単なる先行タスクIDの配列化だけでなく、リンクタイプ(FS, SS, FF, SF)やラグタイムまで数値として正確に抽出する。

3. 完全な型安全とエスケープ

  • 文字列は必ずエスケープ処理を通し、日付はISO 8601形式(`YYYY-MM-DDTHH:mm:ss`)に正規化してWeb側(JavaScript)でのパース負荷をゼロにする。

—

3. 【プロダクションコード】WBS JSONシリアライザー

以下のコードを、Projectの標準モジュールにそのまま貼り付けてほしい。外部ライブラリ(JScriptやCOMオブジェクト)に頼らず、VBAのネイティブコードのみで安全かつ高速にJSON文字列を生成する。

Option Explicit

‘ ==============================================================================
‘ 処理名: MS Project WBS JSON シリアライザ
‘ 概要 : アクティブプロジェクトの全タスク(階層・依存関係含む)をJSONに変換
‘ ==============================================================================
Public Sub ExportWBSToJson()
Dim jsonBuffer As String
Dim targetPath As String

On Error GoTo ErrorHandler

‘ 処理開始時刻の記録(パフォーマンス計測用)
Dim startTime As Double
startTime = Timer

‘ 出力先パスの指定(デスクトップに ‘wbs_data.json’ を出力)
targetPath = CreateObject(“WScript.Shell”).SpecialFolders(“Desktop”) & “\wbs_data.json”

‘ JSONルートの構築開始
jsonBuffer = “{”
jsonBuffer = jsonBuffer & “””” & “project” & “””:{”
jsonBuffer = jsonBuffer & “””” & “name” & “””:””” & EscapeJson(ActiveProject.Name) & “””,”
jsonBuffer = jsonBuffer & “””” & “exportedAt” & “””:””” & Format(Now, “yyyy-mm-ddThh:nn:ss”) & “””,”
jsonBuffer = jsonBuffer & “””” & “tasks” & “””:[”

‘ タスク群のシリアライズ(サマリーおよび通常タスクの走査)
Dim t As Task
Dim taskJsonCollection As String
taskJsonCollection = “”

For Each t In ActiveProject.Tasks
If Not t Is Nothing Then
‘ 削除済みタスクやマイルストーン等の判定を含めたオブジェクト化
taskJsonCollection = taskJsonCollection & SerializeTask(t) & “,”
End If
Next t

‘ 末尾のカンマを除去
If Len(taskJsonCollection) > 0 Then
taskJsonCollection = Left(taskJsonCollection, Len(taskJsonCollection) – 1)
End If

jsonBuffer = jsonBuffer & taskJsonCollection
jsonBuffer = jsonBuffer & “]}”
jsonBuffer = jsonBuffer & “}”

‘ ファイルへの書き出し(UTF-8 Without BOM で書き出すのがWeb連携の鉄則)
Call WriteTextFile(targetPath, jsonBuffer)

MsgBox “WBSのJSONシリアライズが完了しました。” & vbCrLf & _
“出力先: ” & targetPath & vbCrLf & _
“処理時間: ” & Format(Timer – startTime, “0.00”) & ” 秒”, vbInformation, “API連携モジュール”
Exit Sub

ErrorHandler:
MsgBox “予期せぬエラーが発生しました: ” & Err.Description, vbCritical, “エラー”
End Sub

‘ ——————————————————————————
‘ 個別タスクのJSONオブジェクト化
‘ ——————————————————————————
Private Function SerializeTask(ByVal t As Task) As String
Dim sb As String
sb = “{”
sb = sb & “””” & “id” & “””:” & t.ID & “,”
sb = sb & “””” & “uniqueId” & “””:” & t.UniqueID & “,”
sb = sb & “””” & “name” & “””:””” & EscapeJson(t.Name) & “””,”
sb = sb & “””” & “wbs” & “””:””” & EscapeJson(t.WBS) & “””,”
sb = sb & “””” & “outlineLevel” & “””:” & t.OutlineLevel & “,”
sb = sb & “””” & “summary” & “””: ” & IIf(t.Summary, “true”, “false”) & “,”
sb = sb & “””” & “milestone” & “””: ” & IIf(t.Milestone, “true”, “false”) & “,”
sb = sb & “””” & “start” & “””:””” & Format(t.Start, “yyyy-mm-ddThh:nn:ss”) & “””,”
sb = sb & “””” & “finish” & “””:””” & Format(t.Finish, “yyyy-mm-ddThh:nn:ss”) & “””,”
sb = sb & “””” & “percentComplete” & “””:” & t.PercentComplete & “,”
sb = sb & “””” & “cost” & “””:” & t.Cost & “,”

‘ 前提条件(依存関係)のシリアライズ
sb = sb & “””” & “predecessors” & “””:[” & GetPredecessorsJson(t) & “]”

sb = sb & “}”
SerializeTask = sb
End Function

‘ ——————————————————————————
‘ 先行タスク(依存関係)の抽出とフォーマット
‘ ——————————————————————————
Private Function GetPredecessorsJson(ByVal t As Task) As String
Dim pred As TaskPredecessor
Dim predStr As String
predStr = “”

On Error Resume Next
For Each pred In t.Predecessors
Dim linkTypeStr As String
Select Case pred.LinkType
Case pjLinkFinishToStart: linkTypeStr = “FS”
Case pjLinkStartToStart: linkTypeStr = “SS”
Case pjLinkFinishToFinish: linkTypeStr = “FF”
Case pjLinkStartToFinish: linkTypeStr = “SF”
Case Else: linkTypeStr = “FS”
End Select

predStr = predStr & “{”
predStr = predStr & “””” & “taskId” & “””:” & pred.ID & “,”
predStr = predStr & “””” & “type” & “””:””” & linkTypeStr & “””,”
predStr = predStr & “””” & “lag” & “””:” & pred.Lag
predStr = predStr & “},”
Next pred
On Error GoTo 0

If Len(predStr) > 0 Then
predStr = Left(predStr, Len(predStr) – 1)
End If

GetPredecessorsJson = predStr
End Function

‘ ——————————————————————————
‘ JSON文字列のエスケープ処理(安全性の担保)
‘ ——————————————————————————
Private Function EscapeJson(ByVal str As String) As String
str = Replace(str, “\”, “\\”)
str = Replace(str, “”””, “\”””)
str = Replace(str, vbCrLf, “\n”)
str = Replace(str, vbCr, “\n”)
str = Replace(str, vbLf, “\n”)
str = Replace(str, vbTab, “\t”)
EscapeJson = str
End Function

‘ ——————————————————————————
‘ UTF-8 (BOMなし) でファイル出力を行うルーチン
‘ ——————————————————————————
Private Sub WriteTextFile(ByVal filePath As String, ByVal content As String)
Dim stream As Object
Set stream = CreateObject(“ADODB.Stream”)

With stream
.Type = 2 ‘ adTypeText
.Charset = “utf-8”
.Open
.WriteText content
.SaveToFile filePath, 2 ‘ adSaveCreateOverWrite
.Close
End With

Set stream = Nothing
End Sub

—

4. Webダッシュボード連携における実務上の注意点

このコードによって生成されたJSONを、ReactやVue.js、あるいはD3.jsやFrappe GanttなどのWebフロントエンドに渡す際、以下のインフラ・設計上の罠に注意してほしい。

① 文字コードの罠(BOM付きUTF-8の排除)

VBAの標準的なファイル出力機能(`Open … For Output`)では、Shift-JISしか吐けないか、強制的にBOM付きUTF-8になってしまい、近代的なWeb APIやモダンブラウザのFetch APIで文字化けを引き起こす。上記のコードでは `ADODB.Stream` を使用し、モダンWeb標準である UTF-8 (Without BOM) で直接ローカルストレージやサーバーの公開ディレクトリへ書き出す設計にしている。

② ローカルファイル連携からWeb API(HTTP POST)への拡張

もし、ファイルを介さずに直接社内Webサーバーやクラウド上のダッシュボード(AWS API Gateway / Azure Functions等)へデータを飛ばしたい場合は、`WriteTextFile` の代わりに `MSXML2.ServerXMLHTTP.6.0` を用いてPOSTリクエストを飛ばすように拡張すればよい。

Dim http As Object
Set http = CreateObject(“MSXML2.ServerXMLHTTP.6.0”)
http.Open “POST”, “https://your-dashboard-api.internal/endpoint”, False
http.setRequestHeader “Content-Type”, “application/json”
http.setRequestHeader “Authorization”, “Bearer YOUR_API_TOKEN”
http.send jsonBuffer

現場のプロジェクトマネージャーから「Projectの動きが重い」「見づらい」という不満が出た時、Excelにエクスポートして二度手間を踏むのはもう終わりだ。このシリアライザを組み込むことで、「Microsoft Projectがバックエンドの強力なWBSエンジン、Webが軽快なUI」という、極めてモダンなハイブリッドアーキテクチャが完成する。

現場の自動化を極め、真に価値のあるエンジニアリングを追求してほしい。

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