XBRLインスタンスファイル解析

ここから実際にEXCEL VBAでの実装の説明に入ります。

ユーザ定義型

まず、ワークシート関数を実装する前に、XBRLインスタンスファイルをユーザ定義型の変数に読み込む処理を実装していきます。
最初に必要なユーザ定義型を3つ定義します。

‘コンテキスト
Public Type TContext
‘コンテキストID
ContextID As String
‘開始日
StartDate As Variant
‘終了日
EndDate As Variant
‘基準日
Instant As Variant
‘シナリオ
Scenario As Variant
‘NonConsolidateシナリオかどうか
ScenarioNonConsolidate As Boolean
End Type

‘ファクト
Public Type TFact
‘要素名
ElementName As String
‘名前空間
ElementNamespace As String
‘コンテキストID
ContextID As String
‘値
Value As Variant
‘単位
Unit As Variant
‘Nil属性
Nil As Boolean
‘デシマル属性
Decimals As Variant
End Type

‘XBRLインスタンス
Public Type TInstance
‘XBRLインスタンスのファイルパス
FilePath As String
‘コンテキスト
Contexts() As TContext
‘ファクト
Facts() As TFact
End Type

TContext型の開始日・終了日・基準日を(Date型にせず)Variant型にしたのは、VBAではDate型の初期値の関係で未設定の場合の扱いが面倒になるからです。値を設定するさいに、Date型の値を設定し、未設定の状態では初期値のEmptyとなるようにします。
TContext型のScenarioNonConsolidateは、シナリオにNonConsolidate要素が含まれているかを示します。EDINETのXBRLインスタンスにおいては、コンテキストのシナリオにNonConsolidate要素が含まれているということは、非連結(単独)のコンテキストであることを示します。

TFact型の値は、要素によって、文字列だけでなく日付・真偽・金額などが設定されるため、Variant型にしています。XBRLインスタンスから読み取った文字列の形式により、適当に型変換して設定します。

TInstance型は、XBRLインスタンスの情報を保持します。インスタンスには、コンテキストとファクト以外にも、参照しているスキーマのリファレンスや、単位やフットノートなどが記述されていますが、このツールでは意図的に無視しています。コンテキストとファクトは動的配列で保持します。

XMLファイルの読み込み

XBRLインスタンスはXMLで記述されています。本ツールでは、Microsoft.XMLDOMを使ってXMLを処理することにします。<

‘パースしたXBRLインスタンスを保持しておく領域
Private parsedInstanceFile(1024) As TInstance

‘指定されたパスのインスタンスファイルを読み込んでパースし、
‘XBRLインスタンス型にして変換する
Function ParseInstanceFile(ByVal path As String) As TInstance

‘モジュールレベル変数に保持されているかチェックし、
‘保持されている場合にはそれを返す
Dim i As Integer

For i = 0 To UBound(parsedInstanceFile)
If (parsedInstanceFile(i).FilePath = path) Then
ParseInstanceFile = parsedInstanceFile(i)
Exit Function
End If
‘FilePathが””のものがあったら、その先はないのでExitする。
If (parsedInstanceFile(i).FilePath = “”) Then
Exit For
End If
Next

‘XMLオブジェクト(インスタンスファイルをセットする)
Dim xml As Object
‘パースしたインスタンスを設定する
Dim newInstance As TInstance

‘XMLファイル(インスタンスファイル)をロードする
Set xml = CreateObject(“Microsoft.XMLDom”)
‘同期処理にする(ロードが完了するまで処理を待つ)
xml.async = False
‘ロードする
xml.Load path
‘インスタンスのメンバーを設定
newInstance.FilePath = path
newInstance.Contexts = GetContexts(xml)
newInstance.Facts = GetFacts(xml)

‘モジュールレベル変数に保管しておく
If (i <= UBound(parsedInstanceFile)) Then
parsedInstanceFile(i) = newInstance
End If

Set xml = Nothing
ParseInstanceFile = newInstance

End Function

ParseInstanceFile関数は、引数でXBRLインスタンスのパスを受け取り、そのパスのファイルをロードして解析(パース)して、TInstance型に格納します。
XMLをロードして解析する処理は重くなるため、一度解析したらモジュールレベルの変数に保存し、同じパスのファイルは何度も解析しないようにします。なお、保存する配列の長さは1025(添字0~1024まで)となっていますが、この長さに深い意味はありません。

この関数のキモは、TInstance型の変数 newInstance のメンバーContextsとFactsに値を設定するところですが、それはそれぞれ下請け関数のGetContexts/GetFacts関数で行います。

コンテキストの取得

XBRLインスタンスをロードしたXMLオブジェクトからコンテキスト(の配列)を取得します。

‘インスタンスファイルのXMLオブジェクトから、コンテキストを取得する
Function GetContexts(ByVal xml As Object) As TContext()

Dim nodeContexts As Collection
Dim result() As TContext
Dim idxNode As Object
Dim i As Integer

Set nodeContexts = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, “context”)

ReDim result(0 To nodeContexts.Count – 1)
i = 0
For Each idxNode In nodeContexts
result(i) = ToContext(idxNode)
i = i + 1
Next
GetContexts = result

End Function

‘XMLノードからTContextに変換する
Function ToContext(ByVal xml As Object) As TContext
Dim result As TContext
Dim element As Object
Dim dateElements As Collection
Dim att As Object
Dim scenarioElements As Collection

‘コンテキストIDの設定
For Each att In xml.Attributes()
If (att.baseName = “id”) Then
result.ContextID = att.Text
Exit For
End If
Next

‘開始日の設定
Set dateElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, “startDate”)
If (dateElements.Count > 0) Then
result.StartDate = CDate(dateElements(1).Text)
End If

‘終了日の設定
Set dateElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, “endDate”)
If (dateElements.Count > 0) Then
result.EndDate = CDate(dateElements(1).Text)
End If

‘基準日の設定
Set dateElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, “instant”)
If (dateElements.Count > 0) Then
result.Instant = CDate(dateElements(1).Text)
End If

‘シナリオ、NonConsolidateシナリオかどうかの設定
Set scenarioElements = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, “scenario”)
If (scenarioElements.Count > 0) Then
result.Scenario = scenarioElements(1).Text
For Each element In scenarioElements(1).getElementsByTagName(“*”)
If (element.baseName = “NonConsolidated”) Then
result.ScenarioNonConsolidate = True
Exit For
End If
Next
End If

ToContext = result

End Function

GetContexts関数は、XMLオブジェクトから、名前空間「http://www.xbrl.org/2003/instance」、名称「context」のノードを取得します。この各ノードが1つのTContext型の変数に該当します。

contextノードは、典型的には以下のようなXMLノードです。

<xbrli:context id=”CurrentYearNonConsolidatedDuration”>
<xbrli:entity>
<xbrli:identifier scheme=”http://info.edinet-fsa.go.jp”>EDINETコード</xbrli:identifier>
</xbrli:entity>
<xbrli:period>
<xbrli:startDate>2010-01-01</xbrli:startDate>
<xbrli:endDate>2010-12-31</xbrli:endDate>
</xbrli:period>
<xbrli:scenario>
<jpfr-oe:NonConsolidated />
</xbrli:scenario>
</xbrli:context>

ToContext関数は、各contextノードからTContext型の変数に変換します。id属性の値をContextIDに、startDateタグの値をStartDateに、endDateタグの値をEndDate、Instantタグの値をInstant、scenarioタグの内容をScenarioにそれぞれ設定していきます。scenarioタグの中にNonConsolidatedという要素があるかないかをチェックし、あった場合にはScenarioNonConsolidateの値をTrueにしています。

scenarioタグの中にNonConsolidatedという要素があるかないかというのは、EDINETのデータの場合、そのコンテキストが連結(Consolidated)なのか非連結(NonConsolidated)なのかを意味しています。

ファクトの取得

XBRLインスタンスをロードしたXMLオブジェクトからファクト(の配列)を取得します。

‘インスタンスファイルのXMLオブジェクトから、ファクトを取得する
Function GetFacts(ByVal xml As Object) As TFact()

Dim xbrlNodes As Object
Dim result() As TFact
Dim element As Object
Dim nodeFacts As Collection
Dim idx As Integer

Set nodeFacts = New Collection
‘xbrlのノード(ルートノード)を取得する
Set xbrlNodes = myGetElementsByTagNameNS(xml, NAMESPACE_XBRLI, “xbrl”)
If (xbrlNodes.Count > 0) Then
‘ルートノードの子ノードには、ファクトのほか、コンテキストや
‘フットノートリンクやSchemaRefなどが定義される。
‘それらの名前空間は特定のものであるため、それ以外の名前空間
‘に属したものをファクトとする。
For Each element In xbrlNodes(1).ChildNodes()
If (element.namespaceURI <> NAMESPACE_LINKBASE And _
element.namespaceURI <> NAMESPACE_XBRLI) Then
nodeFacts.Add element
End If
Next
End If

ReDim result(0 To nodeFacts.Count – 1)
idx = 0
For Each element In nodeFacts
result(idx) = ToFact(element)
idx = idx + 1
Next
GetFacts = result

End Function

‘XMLノードからTFactに変換する
Function ToFact(ByVal xml As Object) As TFact
Dim result As TFact
Dim att As Object

result.ElementName = xml.baseName
result.ElementNamespace = xml.namespaceURI
For Each att In xml.Attributes()
If (att.baseName = “contextRef”) Then
result.ContextID = att.Text
ElseIf (att.baseName = “unitRef”) Then
result.Unit = att.Text
ElseIf (att.baseName = “decimals”) Then
result.Decimals = att.Text
ElseIf (att.namespaceURI = NAMESPACE_XMLSI And att.baseName = “nil”) Then
result.Nil = CBool(att.Text)
End If
Next
result.Value = IIf(result.Nil, CVErr(xlErrNull), ToValue(xml.Text))

ToFact = result
End Function

‘TFactのValueに設定する値を、文字列の形式から適切な型に変換して返す
Private Function ToValue(ByVal str As String) As Variant

If (IsNumeric(str)) Then
ToValue = Val(str)
ElseIf (UCase(str) = “TRUE” Or UCase(str) = “FALSE”) Then
ToValue = CBool(str)
ElseIf (IsDate(str)) Then
ToValue = CDate(str)
Else
ToValue = str
End If

End Function

GetFacts関数は、XMLオブジェクトから、xbrlノード(ルートノード)の子ノードのうちFactを表すノードを取得します。各ノードが1つのTFact型の変数に該当します。

ToFact関数は、そのノードをTFactに変換します。contextRef属性、unitRef属性、decimals属性、nil属性の値をそれぞれメンバー変数に格納し、ノードのTextをValueに設定します。Value設定するときに、ToValue関数で文字列のフォーマットからデータ型を判定し、キャストしています。

XBRLインスタンスファイル解析」への3件のフィードバック

  1. せあや

    はじめまして。こちらのサイトや「有報キャッチャーEXCELツール」のソースを参考にさせていただき、
    XBRLをエクセルに取り込むツールを自作で作成しておりますが苦戦しておりまして質問させてください。
    こちらに記載されておりますコンテキストの取得「GetContexts」で使用されている「 myGetElementsByTagNameNS」ですが、これはVBAの関数には無いかと思います。そちらが使えなかったため「getElementsByTagName」を使用しようとしたのですが、戻り値がオブジェクトのためCollectionに代入することができず苦戦しております。
    「有報キャッチャーEXCELツール」のCContextクラス、XmlUtilモジュールをインポートしてみたもののうまく活用できません。できれば「myGetElementsByTagNameNS」の定義を教えていただけないでしょうか。

    返信
  2. せあや

    早速のご返信、ありがとうございます!
    大変助かります!ただいまダウンロードさせていただきました!
    こちらを参考にして勉強させていただきます。ありがとうございました。

    返信

せあや にコメントする コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>