【VBA】XMLファイルをExcelに読み込み・生成する方法(コピペOK)

VBA
スポンサーリンク

記事ID: 110
タイトル: 【VBA】XMLファイルをExcelに読み込み・生成する方法(コピペOK)
カテゴリ: 外部連携
一次キーワード: VBA XML 読み込み 作成
想定読者: 他システムとXML形式でデータをやり取りする必要がある実務担当者(VBA初心者〜初級者)
検索意図: VBAでXMLファイルをExcelに読み込む方法、ExcelのデータからXMLファイルを生成する方法を知りたい
読者の悩み(1文): 他システムから出力されたXMLをExcelに取り込みたい、またはExcelのデータをXMLで出力したいが、やり方が分からない。
読了後にできること(1文): MSXML2.DOMDocument60を使って、XMLファイルの読み込み・解析・生成がVBAでできるようになる。
前提条件:
  - Excel版: Excel 2016以降 / Microsoft 365
  - OS: Windows 10/11
  - 保存形式: .xlsm(マクロ有効ブック)
  - 貼り付け場所: 標準モジュール
  - 実行方法: マクロ実行(Alt+F8)
更新日: 2026-03-18

スポンサーリンク

この記事でわかること

  • XMLファイルをVBAで読み込んでExcelシートに一覧表として展開できる
  • ExcelのデータからXMLファイルを自動生成できる
  • MSXML2.DOMDocument60の基本的な使い方(Load / Save / getElementsByTagName)が分かる

対象: Excel 2016以降 / Microsoft 365、Windows 10/11

どんな場面で使う?

  • 他部門のシステムから出力されたXMLファイルを自動でExcelに取り込みたいとき
  • Excelの一覧データをXML形式で他システムに渡したいとき
  • XMLの特定タグだけ抽出してExcelの表に展開したいとき
  • 毎日定時で出力されるXMLファイルを朝イチで自動取込する仕組みを作りたいとき

完成イメージ(Before / After)

Before(XMLファイルの中身):


<?xml version="1.0" encoding="UTF-8"?>
<products>
  <product id="P001">
    <name>ボールペン</name>
    <price>150</price>
    <stock>200</stock>
  </product>
  <product id="P002">
    <name>ノート</name>
    <price>300</price>
    <stock>150</stock>
  </product>
  <product id="P003">
    <name>消しゴム</name>
    <price>80</price>
    <stock>500</stock>
  </product>
</products>

After(Excelシートに展開):

A列(商品ID) B列(商品名) C列(価格) D列(在庫)
P001 ボールペン 150 200
P002 ノート 300 150
P003 消しゴム 80 500

XMLファイルを指定して実行するだけで、タグの中身がセルに一覧表として展開される。逆に、Excelの一覧表からXMLファイルを自動生成することもできる。

自分も以前、他部門のシステムからXML形式で出力されるデータを毎回手作業でExcelに転記していた。タグの中身を1つずつコピーしてセルに貼る作業が正直めんどくさかった。VBAでXML読み込みを自動化してからは、ファイルを指定して実行するだけで一覧表が完成するようになった。同じようにXMLの手作業転記で時間を溶かしている人が、この記事でサクッと自動化できればうれしい。

テキストファイルの取り込みが初めてなら、まず テキストファイル(txt/log)をExcelに取り込む方法 で基本を押さえておくとスムーズに進められる。

実行前の準備

バックアップを取る

実務版コードはXMLファイルを新規生成する(既存ファイルは上書きされる)。実行前に元データのコピーを別フォルダに保存しておく。

サンプルXMLファイルを用意する

読み込みテスト用に、以下のXMLをメモ帳で作成して保存する。

  1. メモ帳を開く
  2. 上の「Before」のXMLコードをコピペする
  3. 「ファイル」→「名前を付けて保存」
  4. ファイル名: products.xml
  5. 文字コード: UTF-8(メモ帳の場合「エンコード」で選択)
  6. 保存先: C:\Test\products.xml(任意のフォルダでOK)

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロが保存できない。

  1. 「ファイル」→「名前を付けて保存」
  2. ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  3. 保存

手順(コピペ → 実行まで約5分)

VBE(コードを書く画面)を開く

  1. Excelで Alt + F11 を押す

標準モジュールを挿入する

  1. VBEのメニュー →「挿入」→「標準モジュール」

コードを貼り付けて実行する

  1. コードウィンドウに、下のコードをそのままコピペする
  2. コード内の xmlPath を実際のXMLファイルのパスに書き換える
  3. Alt + F8 → マクロ名を選んで「実行」

コードの先頭に Option Explicit を入れておくと、変数名のタイプミスをVBAが検出してくれる。標準モジュールの1行目に書いておくのがおすすめ。

コード(最小版)– XMLファイルをExcelに読み込む

サンプルXMLファイル(products.xml)を読み込んで、アクティブシートに一覧表として展開する。まずはこれで動きを確認する。


Option Explicit

'============================================================
' ■ XMLファイルをExcelに読み込む(最小版)
'   → 指定したXMLファイルを解析し、シートに一覧表として展開
'============================================================
Sub ImportXML()

    '--- ★書き換えポイント: XMLファイルのパス ---
    Dim xmlPath As String
    xmlPath = "C:\Test\products.xml"

    '--- XMLオブジェクトを生成
    Dim xmlDoc As Object
    Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")

    '--- 同期読み込みに設定(重要: これがないとデータが空になる)
    xmlDoc.Async = False

    '--- XMLファイルを読み込む
    If xmlDoc.Load(xmlPath) = False Then
        MsgBox "XMLの読み込みに失敗しました。" & vbCrLf & _
               xmlDoc.parseError.reason, vbExclamation
        Exit Sub
    End If

    '--- product要素を全て取得
    Dim products As Object
    Set products = xmlDoc.getElementsByTagName("product")

    If products.Length = 0 Then
        MsgBox "product要素が見つかりませんでした。", vbExclamation
        Exit Sub
    End If

    '--- ヘッダーを書き込む
    Dim ws As Worksheet
    Set ws = ActiveSheet
    ws.Cells(1, 1).Value = "商品ID"
    ws.Cells(1, 2).Value = "商品名"
    ws.Cells(1, 3).Value = "価格"
    ws.Cells(1, 4).Value = "在庫"

    '--- データを書き込む
    Dim i As Long
    Dim node As Object
    For i = 0 To products.Length - 1
        Set node = products.Item(i)
        ws.Cells(i + 2, 1).Value = node.getAttribute("id")
        ws.Cells(i + 2, 2).Value = node.selectSingleNode("name").Text
        ws.Cells(i + 2, 3).Value = node.selectSingleNode("price").Text
        ws.Cells(i + 2, 4).Value = node.selectSingleNode("stock").Text
    Next i

    MsgBox products.Length & " 件の商品データを読み込みました。", vbInformation

    Set xmlDoc = Nothing

End Sub

書き換えポイント

変数 説明 初期値
xmlPath XMLファイルのフルパス C:\Test\products.xml
getElementsByTagName("product") 取得する要素のタグ名 "product"
getAttribute("id") 取得する属性名 "id"
selectSingleNode("name") 取得する子要素のタグ名 "name", "price", "stock"

ポイント: xmlDoc.Async = False を忘れると、読み込みが完了する前にデータを取得しようとして空になる。これが最もハマりやすいポイント。

最小版ではselectSingleNodeの結果がNothingの場合(子要素が存在しない場合)にエラーになる。サンプルXMLで動作確認したら、実務版に進むとよい。

読み込む前にファイルの存在を確認したい場合は ファイルやフォルダの存在を確認してから処理する方法 を参照。

コード(実務版)– エラー処理付きXML読み込み+XML生成

実務では「読み込み」と「生成」の両方が必要になることが多い。このコードは以下の2つのマクロを含む。

  1. ImportXMLEx: エラー処理・ファイル存在チェック付きのXML読み込み
  2. ExportToXML: ExcelのデータからXMLファイルをUTF-8で生成

この方法を覚えてからは、他システムから毎日出力されるXMLファイルを朝イチで自動取込するマクロを組んで、始業前にデータが揃うようにしている。チームからも「朝来たらデータが入ってるの助かる」と言われるようになった。

※ XMLファイルの生成時、指定パスに既存ファイルがある場合は上書きされます。実行前にバックアップを取ってください。


Option Explicit

'============================================================
' ■ XMLファイルをExcelに読み込む(実務版・エラー処理付き)
'   → ファイル存在チェック・エラー処理・処理件数の表示
'============================================================
Sub ImportXMLEx()

    '--- ★書き換えポイント ---
    Dim xmlPath As String
    xmlPath = "C:\Test\products.xml"

    Dim startRow As Long
    startRow = 2                '← データの開始行(1行目はヘッダー)
    '--- ★ここまで ---

    '--- ファイルの存在チェック(ここが追加)
    If Dir(xmlPath) = "" Then
        MsgBox "XMLファイルが見つかりません。" & vbCrLf & _
               xmlPath, vbExclamation
        Exit Sub
    End If

    '--- XMLオブジェクトを生成
    Dim xmlDoc As Object
    Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")
    xmlDoc.Async = False
    xmlDoc.ValidateOnParse = False  '← DTD検証をスキップ(高速化)

    '--- XMLファイルを読み込む
    If xmlDoc.Load(xmlPath) = False Then
        MsgBox "XMLの解析に失敗しました。" & vbCrLf & _
               "行: " & xmlDoc.parseError.Line & vbCrLf & _
               "理由: " & xmlDoc.parseError.reason, vbExclamation
        Exit Sub
    End If

    '--- product要素を取得
    Dim products As Object
    Set products = xmlDoc.getElementsByTagName("product")

    If products.Length = 0 Then
        MsgBox "product要素が見つかりませんでした。", vbExclamation
        Exit Sub
    End If

    '--- シートの準備
    Dim ws As Worksheet
    Set ws = ActiveSheet

    '--- ヘッダーを書き込む
    ws.Cells(1, 1).Value = "商品ID"
    ws.Cells(1, 2).Value = "商品名"
    ws.Cells(1, 3).Value = "価格"
    ws.Cells(1, 4).Value = "在庫"

    '--- データを書き込む(エラー処理付き)(ここが追加)
    Dim i As Long
    Dim node As Object
    Dim errCount As Long
    errCount = 0

    For i = 0 To products.Length - 1
        Set node = products.Item(i)

        On Error Resume Next

        ws.Cells(i + startRow, 1).Value = node.getAttribute("id")
        ws.Cells(i + startRow, 2).Value = node.selectSingleNode("name").Text
        ws.Cells(i + startRow, 3).Value = CLng(node.selectSingleNode("price").Text)
        ws.Cells(i + startRow, 4).Value = CLng(node.selectSingleNode("stock").Text)

        If Err.Number <> 0 Then
            errCount = errCount + 1
            ws.Cells(i + startRow, 1).Value = "(読み取りエラー: 行 " & i + 1 & ")"
            Err.Clear
        End If

        On Error GoTo 0
    Next i

    '--- 結果表示
    Dim msg As String
    msg = products.Length & " 件の商品データを読み込みました。"
    If errCount > 0 Then
        msg = msg & vbCrLf & errCount & " 件でエラーが発生しました(該当行を確認してください)。"
    End If
    MsgBox msg, vbInformation

    Set xmlDoc = Nothing

End Sub

'============================================================
' ■ ExcelデータからXMLファイルを生成(実務版)
'   → アクティブシートのA〜D列のデータをXML形式で保存
'   → A列: 商品ID、B列: 商品名、C列: 価格、D列: 在庫
'   → ADODB.StreamでUTF-8(BOM付き)保存
'============================================================
Sub ExportToXML()

    '--- ★書き換えポイント ---
    Dim outputPath As String
    outputPath = "C:\Test\products_output.xml"

    Dim startRow As Long
    startRow = 2                '← データの開始行(1行目はヘッダー)

    Dim idCol As Long
    idCol = 1                   '← 商品IDの列(A列=1)

    Dim nameCol As Long
    nameCol = 2                 '← 商品名の列(B列=2)

    Dim priceCol As Long
    priceCol = 3                '← 価格の列(C列=3)

    Dim stockCol As Long
    stockCol = 4                '← 在庫の列(D列=4)
    '--- ★ここまで ---

    Dim ws As Worksheet
    Set ws = ActiveSheet

    '--- 最終行を取得
    Dim lastRow As Long
    lastRow = ws.Cells(ws.Rows.Count, idCol).End(xlUp).Row

    If lastRow < startRow Then
        MsgBox "データがありません。", vbExclamation
        Exit Sub
    End If

    '--- XMLオブジェクトを生成
    Dim xmlDoc As Object
    Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")

    '--- XML宣言を追加
    Dim pi As Object
    Set pi = xmlDoc.createProcessingInstruction("xml", "version=""1.0"" encoding=""UTF-8""")
    xmlDoc.appendChild pi

    '--- ルート要素を作成
    Dim root As Object
    Set root = xmlDoc.createElement("products")
    xmlDoc.appendChild root

    '--- データ行をループしてXML要素を作成
    Dim r As Long
    Dim productNode As Object
    Dim childNode As Object
    Dim textNode As Object
    Dim processCount As Long
    processCount = 0

    For r = startRow To lastRow

        '--- product要素を作成
        Set productNode = xmlDoc.createElement("product")

        '--- id属性を設定
        productNode.setAttribute "id", CStr(ws.Cells(r, idCol).Value)

        '--- name子要素
        Set childNode = xmlDoc.createElement("name")
        Set textNode = xmlDoc.createTextNode(CStr(ws.Cells(r, nameCol).Value))
        childNode.appendChild textNode
        productNode.appendChild childNode

        '--- price子要素
        Set childNode = xmlDoc.createElement("price")
        Set textNode = xmlDoc.createTextNode(CStr(ws.Cells(r, priceCol).Value))
        childNode.appendChild textNode
        productNode.appendChild childNode

        '--- stock子要素
        Set childNode = xmlDoc.createElement("stock")
        Set textNode = xmlDoc.createTextNode(CStr(ws.Cells(r, stockCol).Value))
        childNode.appendChild textNode
        productNode.appendChild childNode

        '--- ルートに追加
        root.appendChild productNode
        processCount = processCount + 1

    Next r

    '--- UTF-8で保存(ADODB.Streamを使用)(ここが追加)
    Dim stream As Object
    Set stream = CreateObject("ADODB.Stream")
    stream.Type = 2             '← adTypeText
    stream.Charset = "UTF-8"
    stream.Open
    stream.WriteText xmlDoc.XML
    stream.SaveToFile outputPath, 2   '← 2 = adSaveCreateOverWrite
    stream.Close

    MsgBox processCount & " 件の商品データをXMLに出力しました。" & vbCrLf & _
           outputPath, vbInformation

    Set stream = Nothing
    Set xmlDoc = Nothing

End Sub

書き換えポイント

変数 説明 初期値
xmlPath 読み込むXMLファイルのパス C:\Test\products.xml
outputPath 出力するXMLファイルのパス C:\Test\products_output.xml
startRow データの開始行 2(1行目はヘッダー)
各列番号(idCol等) データの列位置 A〜D列(1〜4)

シート構成の例

A列(商品ID) B列(商品名) C列(価格) D列(在庫)
P001 ボールペン 150 200
P002 ノート 300 150
P003 消しゴム 80 500

XML処理の実行結果をログに残したい場合は マクロの実行ログをファイルに自動記録する方法 を参照。

よくある落とし穴5選

1. Async = False を設定し忘れてデータが空になる

原因: DOMDocument60のAsync既定値はTrue(非同期読み込み)。非同期だと、Loadが完了する前に次の行が実行されるため、データが空のまま処理が進む。

対策: Load前に必ず xmlDoc.Async = False を設定する。


' NG: Asyncを設定し忘れ → データが空
Dim xmlDoc As Object
Set xmlDoc = CreateObject("MSXML2.DOMDocument.6.0")
xmlDoc.Load "C:\Test\products.xml"  '← 非同期で読み込み開始
Debug.Print xmlDoc.getElementsByTagName("product").Length  '← 0になる

' OK: Async = False を設定
xmlDoc.Async = False
xmlDoc.Load "C:\Test\products.xml"
Debug.Print xmlDoc.getElementsByTagName("product").Length  '← 正しい件数

2. XMLのエンコーディング宣言と実際の保存形式が不一致で文字化けする

自分もこれで1時間溶かした。XMLファイルの先頭に encoding="UTF-8" と書いてあるのに、実際にはShift-JISで保存されていた。Loadは通るのに日本語が全部文字化けしていて、原因に気づくまでコードばかり疑ってしまった。

原因: XML宣言のencoding属性と、ファイルの実際のエンコーディングが一致していない。

対策: メモ帳で保存する際に文字コードを「UTF-8」に指定する。XML宣言の encoding="UTF-8" と保存形式を必ず一致させる。

3. 名前空間付きのXMLでgetElementsByTagNameが0件になる

原因: XMLに xmlns="http://example.com" のような既定の名前空間(デフォルト名前空間)が設定されている場合、getElementsByTagNameでは要素を取得できない。名前空間は「XMLの要素がどの定義に属するか」を示す仕組みで、業務システムが出力するXMLに付いていることが多い。

対策: selectNodesとXPathを使い、setPropertyで名前空間のプレフィックスを指定する。


' 名前空間付きXMLの場合の対処法
xmlDoc.setProperty "SelectionLanguage", "XPath"
xmlDoc.setProperty "SelectionNamespaces", "xmlns:ns='http://example.com'"

' nsプレフィックスを付けてselectNodesで取得
Dim nodes As Object
Set nodes = xmlDoc.selectNodes("//ns:product")
Debug.Print nodes.Length  '← 正しい件数が取得できる

4. Saveで保存するとUTF-16になり、他システムで文字化けする

原因: DOMDocument60の .Save メソッドは、XML宣言に encoding="UTF-8" と書いてあっても、実際にはUTF-16で保存することがある。UTF-16のXMLを受け取るシステム側で文字化けやパースエラーが発生する。

対策: 実務版コードのように、ADODB.Streamを使って明示的にUTF-8で保存する。


' NG: xmlDoc.Save でUTF-16になる場合がある
xmlDoc.Save "C:\Test\output.xml"

' OK: ADODB.StreamでUTF-8を明示
Dim stream As Object
Set stream = CreateObject("ADODB.Stream")
stream.Type = 2
stream.Charset = "UTF-8"
stream.Open
stream.WriteText xmlDoc.XML
stream.SaveToFile "C:\Test\output.xml", 2
stream.Close

5. parseErrorを確認せず、読み込み失敗の原因が分からない

原因: LoadメソッドはXML解析に失敗するとFalseを返すだけで、VBAのランタイムエラーは発生しない。戻り値を無視すると、なぜ失敗したか分からないまま止まる。

対策: Loadの戻り値をチェックし、Falseなら xmlDoc.parseError.reason(理由)と xmlDoc.parseError.Line(エラー行)を表示する。


If xmlDoc.Load(xmlPath) = False Then
    MsgBox "XMLの解析に失敗しました。" & vbCrLf & _
           "行: " & xmlDoc.parseError.Line & vbCrLf & _
           "理由: " & xmlDoc.parseError.reason, vbExclamation
    Exit Sub
End If

VBAでXMLの読み込みデータが空になるときの対処法

「XML.Loadを実行したのにデータが空になる」という場合、原因は Async = False の設定漏れだ。DOMDocument60の既定値はTrue(非同期)なので、Loadが完了する前に次の処理が動いてしまう。Load前に必ず xmlDoc.Async = False を入れる。

VBAでXMLの日本語が文字化けするときの対処法

「XMLを読み込んだら日本語が化ける」という場合、原因はXML宣言のencoding属性と実際のファイルのエンコーディングが不一致だ。encoding=UTF-8と宣言しているならファイルもUTF-8で保存する。メモ帳で保存する際にエンコードをUTF-8に指定すれば解決する。

FAQ

Q1: 参照設定は必要?

不要。CreateObject("MSXML2.DOMDocument.6.0") でレイトバインディングすれば参照設定なしで動く。ただし、VBEで入力補完(IntelliSense)を使いたい場合は、VBEメニュー「ツール」→「参照設定」→「Microsoft XML, v6.0」にチェックを入れるとよい。

Q2: XMLの特定の要素だけ読み込みたい場合は?

selectNodes でXPath式を使えば特定の要素だけ抽出できる。


' 全商品の名前だけ取得
Dim nameNodes As Object
Set nameNodes = xmlDoc.selectNodes("//product/name")

Dim i As Long
For i = 0 To nameNodes.Length - 1
    Debug.Print nameNodes.Item(i).Text
Next i

XPathでは条件指定もできる。例えば //product[price>=200] で価格200以上の商品だけ取得できる。

Q3: 大きなXMLファイル(数万行)でも処理できる?

DOMDocument60はファイル全体をメモリに読み込む方式のため、数十MBを超えるXMLではメモリ不足になる可能性がある。一般的な業務データ(数千〜数万行程度)であれば問題ない。

Q4: CSVとXMLのどちらで出力すべき?

データが単純な表形式(行と列だけ)ならCSVが軽量で扱いやすい。階層構造があるデータ(入れ子のカテゴリ、属性付きデータ等)はXMLが向いている。CSVの取り込みは CSVファイルをExcelに正しく取り込む方法 を参照。

Q5: 生成したXMLのBOM(バイトオーダーマーク)が問題になる場合は?

ADODB.StreamでUTF-8保存すると、ファイル先頭にBOM(3バイトの識別子)が付く。一部のシステムではBOM付きUTF-8を正しく処理できない場合がある。BOMなしで保存するには、一度バイナリとして読み直してBOM部分をスキップして書き出す方法がある。


' BOMなしUTF-8で保存する方法
Dim stmUTF8 As Object
Set stmUTF8 = CreateObject("ADODB.Stream")
stmUTF8.Type = 2
stmUTF8.Charset = "UTF-8"
stmUTF8.Open
stmUTF8.WriteText xmlDoc.XML

'--- バイナリモードで読み直し、BOM(先頭3バイト)をスキップ
stmUTF8.Position = 0
stmUTF8.Type = 1  '← adTypeBinary
stmUTF8.Position = 3  '← BOMの3バイトをスキップ

Dim binData() As Byte
binData = stmUTF8.Read

'--- BOMなしで書き出し
Dim stmOut As Object
Set stmOut = CreateObject("ADODB.Stream")
stmOut.Type = 1
stmOut.Open
stmOut.Write binData
stmOut.SaveToFile outputPath, 2
stmOut.Close
stmUTF8.Close

まとめ

  • XML読み込み: MSXML2.DOMDocument.6.0 の Load → getElementsByTagName でデータ取得 → セルに書き込み
  • XML生成: createElement / createTextNode でツリー構築 → ADODB.Stream で UTF-8 保存
  • 必須設定: Async = False(同期読み込み)を忘れない
  • エンコーディング: XML宣言の encoding と実際の保存形式を一致させる
  • エラー確認: parseError で読み込み失敗の原因を必ず確認する

関連記事

次にやりたくなること

コメント

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