MS Projectを「孤島」から「エコシステム」へ。カスタムフィールドをJSONで繋ぐ、極限のデータ連携術
こんにちは。現場の泥臭い自動化から、大規模システムのアーキテクチャ設計まで渡り歩いてきた、あなたの頼れる先輩エンジニアです。
「マクロの記録」でボタン一つ動かせるようになった。
「基本的なループ処理」でセルの値を書き換えられるようになった。
ここまで来られたあなたは、もう初級者ではありません。しかし、もしあなたが「MS Projectで管理している複雑なカスタムフィールドのデータを、Webシステムや他のツールと連携させたい」と願っているなら、まだ大きな壁が立ちはだかっているはずです。
MS Projectのカスタムフィールド(`Text1`や`Number1`など)は、非常に便利ですが、外部から見ると「中身が何かわからない、ただの箱」に過ぎません。この「意味(セマンティクス)の欠如」を解決し、データをモダンなJSON形式でやり取りする技術を身につければ、あなたの業務自動化は「単なる作業の効率化」から「システム間の統合」へと進化します。
今日は、そのための極限の知見を、コードと共に伝授しましょう。
1. なぜ「JSON」なのか? MS Projectの構造的課題を理解する
MS Projectのカスタムフィールドは、内部的には以下のような構造になっています。
- Text1: “プロジェクトA”
- Number1: 100
- Date1: 2023/12/31
外部システム(例えば、Pythonで動くAI解析ツールや、Reactで作られたダッシュボード)にとって、`Text1`が「プロジェクト名」なのか「担当者名」なのかは、プロジェクトファイルを開かない限り分かりません。
これを解決するのがJSON(JavaScript Object Notation)です。
{
"TaskName": "設計フェーズ",
"ProjectStatus": "進行中",
"Priority": 1
}
このように、「フィールドのインデックス(Text1)」を「意味のあるキー(ProjectStatus)」に変換(シリアライズ)すること。これが、今回のミッションの核心です。
2. アーキテクチャの設計図:マッピング・ディクショナリ
いきなりコードを書くのは二流です。一流はまず、「どのフィールドが、どの名前になるのか」という設計図(マッピング)を定義します。
VBAには、キーと値をペアで保持できる`Scripting.Dictionary`という非常に強力なオブジェクトがあります。これを使って、「`Text1` は `Status` である」という定義を行います。
概念図
1. Source (MS Project): `Task.Text1` $\rightarrow$ `”完了”`
2. Mapping Layer: `Text1` $\rightarrow$ `”TaskStatus”`
3. Target (JSON): `{“TaskStatus”: “完了”}`
3. 実装:カスタムフィールドをJSONへエクスポートする
それでは、実際に動くコードを見ていきましょう。ここでは、タスクの情報を抽出し、JSON形式の文字列を生成するロジックを実装します。
※このコードを動かすには、VBAエディタの [ツール] $\rightarrow$ [参照設定] から 「Microsoft Scripting Runtime」 にチェックを入れてください。
Option Explicit
''' <summary>
''' タスクのカスタムフィールドをJSON形式にエクスポートする
''' </summary>
Public Sub ExportTasksToJson()
Dim task As Task
Dim jsonDict As Object
Dim taskList As Object
Dim mapping As Object
Dim jsonString As String
Dim i As Integer
' 1. マッピングの定義 (ここが設計の肝!)
' Text1 = Status, Text2 = Category, Number1 = CostIndex と定義
Set mapping = CreateObject("Scripting.Dictionary")
mapping.Add "Text1", "TaskStatus"
mapping.Add "Text2", "TaskCategory"
mapping.Add "Number1", "CostIndex"
' 2. JSON構造の構築用オブジェクト
' 本来はJSONライブラリを使うのがベストだが、今回は概念理解のため
' 文字列結合による簡易的なシリアライザを実装する
Set taskList = CreateObject("Scripting.Dictionary")
On Error Resume Next ' タスクが未完了(削除済み等)の場合のガード
For Each task In ActiveProject.Tasks
' 有効なタスクのみ処理
If Not task Is Nothing Then
Dim taskData As String
taskData = "{"
' マッピングに基づいてカスタムフィールドを走査
Dim key As Variant
Dim fieldName As String
Dim fieldVal As Variant
i = 0
For Each key In mapping.Keys
fieldName = mapping(key)
' フィールドの値を取得 (Text1, Text2... と動的に指定)
fieldVal = GetFieldValue(task, key)
' JSONのプロパティを追加
taskData = taskData & """" & fieldName & """: """ & CStr(fieldVal) & """"
' 最後の要素でなければカンマを打つ
If i < mapping.Count - 1 Then taskData = taskData & ","
i = i + 1
Next key
taskData = taskData & "}"
' タスクリストに蓄積(本来は配列やコレクションで管理)
' 今回は簡易的に、一つの大きな文字列として結合していく
If jsonString = "" Then
jsonString = "[" & taskData
Else
jsonString = jsonString & "," & taskData
End If
End If
Next task
jsonString = jsonString & "]"
On Error GoTo 0
' 3. 結果の出力(イミディエイトウィンドウ)
Debug.Print jsonString
MsgBox "エクスポート完了!イミディエイトウィンドウを確認してください。", vbInformation
End Sub
''' <summary>
''' フィールド名を元に、動的にタスクのプロパティを取得するヘルパー関数
''' </summary>
Private Function GetFieldValue(targetTask As Task, fieldName As String) As Variant
' fieldName は "Text1", "Number1" などの形式を想定
' VBAのExecuteメソッド等を使う方法もあるが、シンプルにプロパティとして扱う
On Error Resume Next
Select Case Left(fieldName, 4)
Case "Text"
GetFieldValue = targetTask.Text & Mid(fieldName, 5) ' 実はText1はプロパティ名が特殊な場合がある
' Project VBAの仕様上、Text1は直接 .Text1 とは書けないため注意
' 以下の手法が最も安全
GetFieldValue = CallByName(targetTask, fieldName, VbGet)
Case "Num"
GetFieldValue = CallByName(targetTask, fieldName, VbGet)
Case "Date"
GetFieldValue = CallByName(targetTask, fieldName, VbGet)
End Select
On Error GoTo 0
End Function
💡 ここが「プロ」の視点:`CallByName` の活用
コードの中で `CallByName` という関数を使いました。これは、「文字列で指定されたプロパティ名を使って、オブジェクトの値を動的に取得する」という魔法のような関数です。
`task.Text1` と書くと、`Text1` という名前を固定で扱うことになりますが、`CallByName(task, “Text1”, VbGet)` と書けば、変数を使って柔軟にアクセスできます。これが、拡張性の高いコードを書くための鍵です。
4. インポートの極意:外部データをMS Projectへ戻す
エクスポートができたら、次はインポートです。
JSONをパース(解析)して、MS Projectの適切なフィールドに値を書き戻します。インポート時に最も注意すべきは、「どのタスクに対して書き込むのか?」という識別子の特定です。
通常、`TaskName`(タスク名)だけでは不十分です。名前が重複する可能性があるからです。実務では、`Unique ID` をキーにすることをお勧めします。
''' <summary>
''' JSON形式のデータを解析し、カスタムフィールドに書き戻す(ロジックの雛形)
''' </summary>
Public Sub ImportFromJson(jsonInput As String)
' 注意: 本来はJSONパースライブラリ(VBA-JSON等)を使用することを強く推奨します
' ここでは、ロジックの流れを解説します
Dim task As Task
Dim targetID As Long
Dim statusVal As String
' 1. JSONをパースしてループ処理(擬似コード)
' For Each item In parsedJson
' targetID = item("UniqueID")
' statusVal = item("TaskStatus")
' 2. 対象タスクを検索
' Set task = FindTaskByID(targetID)
' 3. 書き戻し
' If Not task Is Nothing Then
' task.Text1 = statusVal
' End If
MsgBox "インポートのロジックを実行しました(デモ用)", vbInformation
End Sub
5. 陥りやすいエラーと、それを回避する「守りの設計」
この領域を扱う際、多くのエンジニアが以下の罠にハマります。
① データ型の不一致(Type Mismatch)
JSONでは `100`(数値)と `”100″`(文字列)は別物です。MS Projectの `Number1` に `”完了”` という文字列を流し込もうとすると、ランタイムエラーが発生します。
対策: 書き込み前に、必ず `IsNumeric` や `IsDate` を使って、フィールドの型とデータの整合性をチェックしてください。
② 存在しないフィールドへのアクセス
プロジェクトの設定によって、カスタムフィールドが有効になっていない場合があります。
対策: `On Error Resume Next` で強引に飛ばすのではなく、事前にフィールドの利用可能性をチェックするか、エラーハンドリングを厳格に行いましょう。
③ パフォーマンスの壁
タスクが数千件あるプロジェクトで、ループの中で一つずつオブジェクトを操作すると、処理が劇的に遅くなります。
対策: 画面更新を停止する `Application.ScreenUpdating = False` はもちろん、大量のデータを扱う場合は、一度配列(Array)にデータを格納してから、最後に一括で書き戻す手法を検討してください。
最後に:一歩先へ進むあなたへ
カスタムフィールドをJSONで制御できるようになると、MS Projectは単なる「スケジュール管理ツール」から、「巨大なデータソース」へと変貌します。
「マクロの記録」を卒業し、こうしたオブジェクトの構造やデータ変換のロジックを理解することは、エンジニアとしての視座を一段引き上げてくれます。最初は難しく感じるかもしれませんが、一つ一つの関数が「なぜ必要なのか」を考えながら進めてみてください。
ここをクリアすれば、Project VBAの基本はバッチリですよ。
さあ、新しい世界へ踏み出しましょう!
