PDFのような日本語ラベルをつけるために、同梱されているスキーマファイルを読み込みます。そのコード例を示します。
スキーマ schema (Oxford Learner’s Dictionaries) という言葉ですが、アウトラインってイメージのようです。このファイルには、勘定科目が列挙されていたり、ほかの定義ファイルへのリンク載っていたりと、確かにそのような目次的なファイルになっています。
こういった感じで、データ以外の定義部分を別のファイルに分けて書くのは、エックス・エム・エル(XML)の仕様です。ほかにも、XBRLだと思ってたらXMLの用語だったというのが、結構ありました。
以下、プログラムの流れとコード例です。
ファイルリンクとエレメントタグを読み込む
スキーマファイル(xsd)には、以下の3種類が載ってますので、これらを読み込みます。
- さらに別のスキーマへのリンク <import />
- ラベルファイルなどへのリンク <linkbaseRef />
- 勘定科目の定義 <element />
XBRLファイルの各勘定科目とラベルですが、elementタグのid属性やname属性でひも付けられているようです。
具体的には、以下のようにひも付いていました。
- XBRLの勘定科目のタグ名 <—> xsdのエレメントのname属性
- xsdのエレメントのid属性 <—> ラベルファイルのlocタグのhref属性
※ href属性は、スキーマファイルパスとid属性がシャープ記号(#)で区切られています。そのid属性の部分とひも付いていました。
スキーマファイルを解析するクラス
スキーマ用の名前空間を定義して、検索したいタグ名・属性名を定義して、リンクやエレメントを取得していきます。
名前空間を定義する
とりあえず、色々なスキーマファイルを見て、必要なものだけ転記して使います。
"""xbrl_namespace.py"""
NS_XSD = {
None: 'http://www.w3.org/2001/XMLSchema',
'xlink': 'http://www.w3.org/1999/xlink',
'link': 'http://www.xbrl.org/2003/linkbase',
}
元の仕様書のページも紹介します。Googleの日本語翻訳が良く効きますので、調べ物をするときは、まずはそういった機能を使って見ています。
「www.w3.org」は、ワールドワイド・ウェブ・コンソーシアム(W3C)という、HTMLやXMLなどの仕様書を作ってる組織です。
World Wide Web Consortium (W3C)
https://www.w3.org/
なので、そのドメインが入ったエックスリンク(xlink)というのは、その仕様書に載っている機能です。
エックス・エム・エル リンキング ランゲージ (エックスリンク) バージョン 1.1
XML Linking Language (XLink) Version 1.1
https://www.w3.org/TR/xlink/
XMLの仕様書のページを開くと、まさにスキーマ(Schema)という言葉があります。スキーマとは何ぞやか?というお話から始まって、仕様書も見ることができます。
エックス・エム・エル テクノロジー ダブリュースリーシー
XML Technology – W3C
https://www.w3.org/standards/xml/
リンク(link)という接頭辞の名前空間もありますが、こちらはXBRLの領域です。名前空間にXBRLの組織のドメイン名が入っています。名前空間の最後には、リンクベース(linkbase)とついています。以下は、仕様書のページです。
エクステンシブル・ビジネス・レポーティング・ランゲージ
(エックス・ビー・アール・エル) 2.1
Extensible Business Reporting Language (XBRL) 2.1
http://www.xbrl.org/Specification/XBRL-2.1/REC-2003-12-31/XBRL-2.1-REC-2003-12-31+corrected-errata-2013-02-20.html#_3.5.2
スキーマファイルを読み込むところ
XBRLのschemaRefタグにあった、xsdファイルを読み込むコード例です。
importタグのschemaLocation属性、linkbaseRefタグのhref属性、elementタグのid属性をキーにして、それぞれの辞書を作っています。
「import_namespace」は、スキーマファイルのimportタグにある「namespace属性」を受け取るためのものです。ほかのスキーマファイルを再帰的に読み込んでいくときに、一応受け取っているだけで、今のところ使っていません。
"""xbrl_xsd.py"""
from collections import OrderedDict
import xbrl_namespace
from xbrl_util import get_etree_obj_from_file
class Parser:
"""xsdファイル解析クラス(勘定科目用)"""
def __init__(self, file, import_namespace=None, file_data=None):
# XMLファイルを読み込む
self.file = file
self.root = get_etree_obj_from_file(self.file, file_data)
# 名前空間(NameSpace)の定義を取得
ns_def = xbrl_namespace.NS_XSD
# タグ名/属性名定義
self.none_import = '{%s}import' % ns_def[None]
self.none_annotation = '{%s}annotation' % ns_def[None]
self.none_appinfo = '{%s}appinfo' % ns_def[None]
self.link_linkbase_ref = '{%s}linkbaseRef' % ns_def['link']
self.xlink_href = '{%s}href' % ns_def['xlink']
self.xlink_role = '{%s}role' % ns_def['xlink']
self.xlink_arcrole = '{%s}arcrole' % ns_def['xlink']
self.tag_element = '{%s}element' % ns_def[None]
# importタグ取得
self.import_urls = self.get_import_tags()
# linkbaseRefタグ取得
self.linkbase_refs = self.get_linkbase_ref_tags()
# elementタグ取得
self.elements = self.get_element_tags()
# 変数削除
del self.root
return
importタグ取得メソッド
ほかのスキーマファイルをインポートしているタグです。schemaLocation属性にファイルパスが入っていますので、これをキーにして辞書を作っています。
ここでインポートされているスキーマファイルには、「jpfr-di」や「jpdei_cor」の接頭辞で定義されていたタグに関係するエレメントが載っていたりします。
ですので、それらの日本語ラベルもつけたい場合は、再帰的にたどって読み込んでいくことになります。そのために取得します。
def get_import_tags(self):
""""インポートファイル取得"""
datas = OrderedDict()
for element in self.root.findall('.//%s' % self.none_import):
od = OrderedDict()
key = None
for (name, value) in element.items():
od.update({name: value})
if name == 'schemaLocation':
key = value
assert key is not None
datas.update({key: od})
return datas
linkbaseRefタグ取得メソッド
ラベルファイルへのリンクが載っているタグです。ほかにもプレゼンテーションファイル(表示)、カルキュレーションファイル(計算)、デフィニションファイル(定義)などへのリンクが載っています。
ファイルの種類は、ロール属性(role)で定義されています。この「ロール role (Oxford Learner’s Dictionaries)」ですが、RPGのロールと同じ意味です。戦士とか魔法使いとか、そういった、何の役割かを表している言葉です。
URLのような長い文字列ですが、よく見ると
labelLinkbaseRef ラベル
presentationLinkbaseRef プレゼンテーション
calculationLinkbaseRef カルキュレーション
definitionLinkbaseRef デフィニション
って書かれています。このロール属性で、必要なファイルだけを選ぶことができます。
コードには検証用のassert文やenumerate()が残っていますが、消すのが手間だったのでそのまま載せています。本当に1つなのかは、良くわかりません。とりあえず、タグの取りこぼしがあったら、気づけるようにしていました。
href属性をキーにして、辞書を作っています。
def get_linkbase_ref_tags(self):
"""リンクベース取得"""
datas = OrderedDict()
for (n_a, annotation) in enumerate(self.root.findall('.//%s' % self.none_annotation), start=1):
# annotationは1つのはず
assert n_a == 1
for (n_b, appinfo) in enumerate(annotation.findall('.//%s' % self.none_appinfo), start=1):
# appinfoは1つのはず
assert n_b == 1
# リンクベース取得
for element in appinfo.findall('.//%s' % self.link_linkbase_ref):
od = OrderedDict()
for (name, value) in element.items():
od.update({name: value})
if name == self.xlink_href:
key = value
assert key is not None
datas.update({key: od})
return datas
elementタグ取得メソッド
エレメントタグは、XBRLファイルとラベルファイルなどをひも付けているタグです。ラベルをつけるためには、id属性とname属性の情報が必要です。
とりあえず、id属性をキーにして辞書を作っています。
def get_element_tags(self):
"""element取得"""
datas = OrderedDict()
for element in self.root.findall('.//%s' % self.tag_element):
od = OrderedDict()
key = None
for (name, value) in element.items():
od.update({name: value})
if name == 'id':
key = value
if key:
datas.update({key: od})
else:
pass
return datas
実際にスキーマファイルを読み込んでみる
メイン関数でスキーマファイルを指定して読み込んでいます。
うまく読み込めていないファイルがあったら、ここで読み込んでみてデバッグしています。
"""xbrl_xsd.py"""
from collections import OrderedDict
import xbrl_namespace
from xbrl_util import get_etree_obj_from_file
def main():
"""モジュールテスト"""
xsd_file = r"*****\jpfr-q1r-E00000-000-2008-06-30-01-2008-08-08.xsd"
obj = Parser(xsd_file)
return
class Parser:
"""xsdファイル解析クラス(勘定科目用)"""
(省略)
if __name__ == '__main__':
main()
その他のスキーマファイルについて
上記のコードは、勘定科目関係のスキーマファイルを読み込む用ですので、もっと別の概念を定義したスキーマファイルを読み込むときは、その用途に応じた読み込みコードを書くことになります。
とりあえず、勘定科目にラベルを付けるだけなら、このコードだけでできました。
さて、ほかのスキーマを読み込む場合ってどんな状況でしょうか。あまり深く追っていませんが、なにかデータを入力して、それをXBRL形式で保存したいときでしょうか。
「それぞれのタグが持ちうるデータ」。これを定義したスキーマファイルもありましたので、これを読み込んでデータ入力の選択肢を作ったり、入力データのチェックを行うことになるのかなって思っています。