cx_OracleでOracleのXMLTYPEを操作する
環境
- python:3.7.1
- cx_Oracle:7.0.0
XMLデータ
今回利用するXMLは郵便番号のデータとしました。
日本郵便のサイトからダウンロードできるデータはcsvのみでしたので、
http://blog.majide.com/2009/03/jp-post-zipcode-xml/
のサイトのデータを利用しました。
<JapanAreaData>
<Prefecture>
<City>
<Area>
<AreaKana>コウキョガイエン</AreaKana>
<AreaName>皇居外苑</AreaName>
<ZipCode>1000002</ZipCode>
</Area>
<Area>
・・・
</Area>
<CityName>千代田区</CityName>
<CityNameKana>チヨダク</CityNameKana>
</City>
<City>
・・・
</City>
<PrefectureKana>トウキョウト</PrefectureKana>
<PrefectureName>東京都</PrefectureName>
</Prefecture>
</JapanAreaData>
OracleのXMLTYPEについて
XMLTYPEはその名の通り、XMLデータを保持する型となります。
XMLのみを保持するCLOB型と考えればわかりやすいでしょうか。
また、XPath式を使用してXMLデータを操作できるため、XMLと非常に相性の良いという特徴を持ちます。
cx_OracleでのXMLTYPEについて
今回扱うデータはXMLと非常にサイズが大きいため、Insertする際はバインド変数を使用する必要があります。
cx_Oracleでバインド変数を使用する際は、カーソルのCursor.setinputsizesでサイズを指定(拡張)する必要があります。
これを指定しない場合、「cx_Oracle.DatabaseError: ORA-01461: LONG値はLONG列にのみバインドできます。」が発生します。
今回はXMLTYPEですので、サイズはcx_Oracle.CLOBを指定します。
cur.setinputsizes(<バインド変数の変数名>=cx_Oracle.CLOB)
ソース
import cx_Oracle
if __name__ == '__main__':
zip_xml = ""
# 郵便番号のxmlファイル
with open('/path/to/jp_zipcode.xml', 'r', encoding='utf-8') as f:
for s_line in f:
zip_xml += s_line
# Oracle接続
conn = cx_Oracle.connect("test_user", "P@ssW0rd", "localhost:1521/xe")
cur = conn.cursor()
# XMLTYPEをバインドするため、cx_Oracle.CLOBを指定する
cur.setinputsizes(zip=cx_Oracle.CLOB)
param = {'zip': zip_xml}
cur.prepare("insert into zip_xml values(xmltype(:zip))")
cur.execute(None, param)
conn.commit()
cur.execute("select extract(zip, '/JapanAreaData/Prefecture/City/Area[ZipCode=1000002]').getClobVal() from zip_xml")
res = cur.fetchone()
print("郵便番号「1000002」のデータ確認")
print(res[0])
conn.close()
実行結果
$ python test_xmltype.py
郵便番号「1000002」のデータ確認
<Area>
<AreaKana>コウキョガイエン</AreaKana>
<AreaName>皇居外苑</AreaName>
<ZipCode>1000002</ZipCode>
</Area>