SSブログ

調べもの:Excel VBAでSAXを使ってXMLデータ表示 [プログラミング学習]

なんとかできた。

きちんとした説明・修正はおいおいするとして。とりあえずメモ。
今回は、ほぼ以下のサイトのコードを流用させていただきました。ありがとうございました。
「Visual Basic を使用した SAX2 アプリケーション作成のジャンプスタート」
http://msdn.microsoft.com/ja-jp/library/ms994347.aspx

まず、仕様。
XMLファイルを読み込んで、要素、要素の値、属性、属性値をシートに表示する
表示は以下のような構成になる。

Spec概要.jpg
大きく要素名、要素の値、属性を表示するフィールドに分かれる。
要素名は階層構造で示す。要素の値と属性は要素名と同一行に表示する。

処理フローは以下の通り。
・ユーザがシートに設置されたボタンを押下する
 -> [ファイルを開く]ダイアログが表示される
・ユーザがXMLファイルを選択する
 複数選択可。XML形式以外のファイルを開いた場合のエラー処理はしない
 -> XMLファイルの内容がExcelシートに表示される

ファイル構成は以下の通り。
ただし、コードを見るとわかるが、実は処理がモジュールでちゃんとわかれていない。
この辺は今後の課題。
プロジェクトビュー.jpg
・ボタン押下処理を実行するモジュール(Module1)
・SAXでの処理を実行するクラスモジュール(ContentHandlerImpl, ErrorHandlerImpl)
(※ErrorHandlerImplは実際には実装せず)

参照として、MS XML 6.0を追加
ソースコードは以下の通り。

Module1
Option Explicit


'共通変数宣言


'共通定数宣言
Const CNT_FILE_TYPE = "XMLファイル"                 '選択するファイルの説明
Const CNT_FILE_EXT = "*.xml"                        '選択するファイルの拡張子

'’***********************************
''
' 概要:ファイルを開くダイアログを表示し、XMLデータの表示を行う
'
Sub ボタン4_Click()
    ' ***********************************
    '変数宣言
    ' ***********************************
    Dim selectFile As Variant
    Dim i As Integer
    
    Dim reader As SAXXMLReader                      'Reads the XML document
    Dim contentHandler As New ContentHandlerImpl    'Receives parsing events
    Dim errorHandler As New ErrorHandlerImpl        'Receive error events
    
    ' ***********************************
    '初期化
    ' ***********************************
    Set reader = New SAXXMLReader
    Set reader.contentHandler = contentHandler      'They work together
    Set reader.errorHandler = errorHandler          'They also work together
    
    ' Sheetのクリア
    ThisWorkbook.ActiveSheet.Cells.Clear
    
    
    ' ***********************************
    '処理
    ' ***********************************
    
    'Application.FileDialog
    'msoFileDialogFilePicker:ファイル
    'msofiledialogfolderpicker :フォルダ
    With Application.FileDialog(msoFileDialogFilePicker)
    
        'ダイアログタイトル名
        .Title = "ファイル選択"
    
        '開いた時に表示されるフォルダ
        .InitialFileName = ThisWorkbook.Path
    
        '複数ファイル選択の可否 True:可能、False:不可
        .AllowMultiSelect = True
    
        'ファイルの種類
        .Filters.Add CNT_FILE_TYPE, CNT_FILE_EXT
        .Filters.Add "すべてのファイル", "*.*"
    
        '選択された場合
        If .Show = -1 Then
    
            '選択されたアイテムでループ
            For Each selectFile In .SelectedItems
    
                reader.parseURL (selectFile)      'Parse the document

            Next
        End If
    End With
    
    ' シート内の列幅自動調整
    ActiveSheet.Select
    Cells.Columns.AutoFit

      
End Sub

ContentHandlerImpl
Option Explicit
'’***********************************
''
' ContentHandlerの実装
'’***********************************
Implements IVBSAXContentHandler

'共通変数宣言
Public intCurrentCol As Integer                         ' 現在データ入力中の行
Const CNT_INITIAL_COL = 2                               ' データ入力を開始する行
Const CNT_INITIAL_ROW = 2                               ' データ入力を開始する列

Public intLastElementRow As Integer                     ' 要素入力の最終列を示す。この右隣の行がNodeValueの入力列。その右隣がAttributeの入力位置
Public intCurrentElementRow As Integer                  ' 現在データ入力中の要素の列。要素の階層に同じ

'’***********************************
''
' 概要:セル位置の初期化
'
'’***********************************
Private Sub Class_Initialize()
    intCurrentCol = CNT_INITIAL_COL
    intCurrentElementRow = CNT_INITIAL_ROW - 1
    intLastElementRow = intCurrentElementRow

End Sub

'’***********************************
''
' 概要:要素・属性の表示
'
'’***********************************
Private Sub IVBSAXContentHandler_startElement(strNamespaceURI As _
  String, strLocalName As String, strQName As String, ByVal attributes As _
  MSXML2.IVBSAXAttributes)
    
    ' ***********************************
    '変数宣言
    ' ***********************************
    Dim i As Integer
    
    ' ***********************************
    '初期化
    ' ***********************************
    intCurrentCol = intCurrentCol + 1                   ' 行を1つ下に移動する
    intCurrentElementRow = intCurrentElementRow + 1     ' 列を1つ右に移動する
    
    ' ***********************************
    '処理
    ' ***********************************
    
    ' 要素の表示列の最後尾より現在の列が右の場合
    If intCurrentElementRow > intLastElementRow Then
        '列を挿入する
        intLastElementRow = intLastElementRow + 1
        ActiveSheet.Range(Cells(intCurrentCol, intLastElementRow), Cells(intCurrentCol, intLastElementRow)).EntireColumn.Insert
    End If
    
    ' 要素の現在列に現在の要素名を格納する
    Cells(intCurrentCol, intCurrentElementRow).Value = strLocalName
    Cells(intCurrentCol, intCurrentElementRow).Font.Color = RGB(0, 255, 0)  'デバッグ用色分け:要素名
    
    ' 属性表示列に現在の属性名と属性値を格納する
    For i = 0 To (attributes.Length - 1)
        Cells(intCurrentCol, intLastElementRow + 2 * i + 2).Value = attributes.getLocalName(i)
        Cells(intCurrentCol, intLastElementRow + 2 * i + 2).Font.Color = RGB(255, 0, 0)         'デバッグ用色分け:属性名
        Cells(intCurrentCol, intLastElementRow + 2 * i + 2 + 1).Value = attributes.getValue(i)
        Cells(intCurrentCol, intLastElementRow + 2 * i + 2 + 1).Font.Color = RGB(255, 0, 0)     'デバッグ用色分け:属性値
    Next
    

End Sub

'’***********************************
''
' 概要:要素の終了
'
'’***********************************
Private Sub IVBSAXContentHandler_endElement(strNamespaceURI As String, _
  strLocalName As String, strQName As String)
    
    ' 列を一つ左に移動する
    intCurrentElementRow = intCurrentElementRow - 1

End Sub

'’***********************************
''
' 概要:要素の値の表示
'
'’***********************************
Private Sub IVBSAXContentHandler_characters(text As String)
    
    ' 先頭文字の文字コードが制御文字・空白でない場合
    If AscB(Left(text, 1)) > &H20 Then
        ' セルに値を表示する
        Cells(intCurrentCol, intLastElementRow + 1).Value = text
        Cells(intCurrentCol, intLastElementRow + 1).Font.Color = RGB(0, 0, 0)                   'デバッグ用色分け:要素値
    End If
End Sub

'’***********************************
''
' (2009/10/24追加)以降、ContentHandlerのその他イベントハンドラの実装
' 処理なし
'
'’***********************************

Private Property Set IVBSAXContentHandler_documentLocator(ByVal RHS As _
  MSXML2.IVBSAXLocator)

End Property

Private Sub IVBSAXContentHandler_endDocument()

End Sub

Private Sub IVBSAXContentHandler_endPrefixMapping(strPrefix As String)

End Sub

Private Sub IVBSAXContentHandler_ignorableWhitespace(strChars As String)

End Sub

Private Sub IVBSAXContentHandler_processingInstruction(target As String, _
  data As String)
    

End Sub

Private Sub IVBSAXContentHandler_skippedEntity(strName As String)

End Sub

Private Sub IVBSAXContentHandler_startDocument()

End Sub

Private Sub IVBSAXContentHandler_startPrefixMapping(strPrefix As String, _
  strURI As String)

End Sub






今回参考にしたサイト
ありがとうございました。

「Excel VBA 入門講座 セルのクリア」
http://excelvba.pc-users.net/fol2/2_6.html

「Excel VBA 入門講座 セルの挿入」
http://excelvba.pc-users.net/fol2/2_7.html

「Excel VBAではじめるクラス入門:CodeZine」
http://codezine.jp/article/detail/499?p=1
参考にしたサンプルがクラス作成前提だったので、今回はこちらを参照しました。
・・・作らなくてもいいとは思うんですが、その裏付けが取れませんでした。orz
作る必要ありますね。継承しないといけませんもの。orz

「セルについて」
http://abcclub.cside.ne.jp/vbahelp_help/dai6.htm
デバッグ用に出力セルに色をつけて、正しく動作するかを確認しました。

「Excel VBA を学ぶなら moug モーグ | 即効テクニック | セル範囲を参照する2(Cellsプロパティ)」
http://www.moug.net/tech/exvba/0050084.htm

「エクセルExcel大事典 VBAマクロ 文字列操作 Instr Strcomp Trim StrConv Chr Asc Format」
http://home.att.ne.jp/zeta/gen/excel/c04p40.htm
不要な改行・空白セル、タブを除くために文字コードで除外しました。
Schemaを使っていないため、ignoreableWhitespeceが使えませんでした。

「Asc 関数、AscW 関数」
http://msdn.microsoft.com/ja-jp/library/zew1e4wc(VS.80).aspx

http://www009.upp.so-net.ne.jp/Mishika/
タグ:Excel VBA XML SAX
nice!(0)  コメント(3)  トラックバック(0) 
共通テーマ:パソコン・インターネット

nice! 0

コメント 3

Yoshi

使わせていただこうと思ったらこんなの出ます。
僕の環境だけでしょうか?
--------------------------------------------------------
コンパイル エラー:

オブジェクト モジュールにはインターフェイス 'documentLocator' 用の 'IVBSAXContentHandler' が必要です。
--------------------------------------------------------
documentLocatorって何・・・。
by Yoshi (2009-10-21 03:45) 

Mishika

〉Yoshiさん
コメントありがとうございます。
今携帯なので、詳細調べられないのですが、参照が上手くいってないように見えます。
追って調べさせていただきます。
by Mishika (2009-10-21 21:58) 

Mishika

>Yoshiさん
発生していた現象ですが、結論から言うと、原因は必要なコードが足りなかったためでした。

ContentHandlerImplはContentHandlerインタフェースを実装しているので、ContentHandlerのすべてのイベントハンドラを実装する必要があります。
実際に私が作成したExcelマクロのファイルでは、中身にコードが記載されていないイベントハンドラが定義されていました。
ただ、上の記事では、ContentHandlerのイベントハンドラのうち、処理をしていないイベントハンドラは記載しておりませんでした。

ContentHandlerImpのコードに、処理をしていないイベントハンドラも追加しましたので、ご参照いただければ幸いです。

最後になりましたが、コメントおよびフィードバックありがとうございました。
by Mishika (2009-10-24 23:06) 

コメントを書く

お名前:[必須]
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0