PythonでXMLの構造を表示してみた

PythonでXMLファイルのタグの親子関係を視覚化してみました。

目次

  1. defusedxmlモジュール
  2. XMLのタグの構造を可視化したい
  3. インデントで階層を表現する例
  4. 罫線で階層を表現する例

defusedxmlモジュール

Pythonの公式ドキュメントのxmlモジュールの節に書かれていますが、XMLにはパーサーを悪用するなどした脆弱性があります。

そこで、安全性の不明なXMLを扱うときは、Pythonの組み込みのxmlモジュールではなくdefusedxmlなどの外部モジュールの使用が推奨されています。(公式のドキュメントに書かれています。)

本投稿では、defusedxmlを使用します。

XMLのタグの構造を可視化したい

XMLのタグには親子関係があります。入れ子で表現されているので一つ一つ読んでいけばわかるのですが、面倒なのでtreeというコマンドの表示のように可視化したいと思います。

サンプルは、このようなXMLです。

<?xml version="1.0" encoding="UTF-8" ?>
<tip>
    <middle id='1'>
        <bottom>content_1</bottom>
    </middle>
    <middle id='2'>
        <bottom>content_20</bottom>
        <bottom>content_21</bottom>
    </middle>
    <middle id='3'>
        content_3
    </middle>
</tip>

インデントで階層を表現する例

階層をPythonのようにインデントで表示してみます。インデントにはタブ文字を使用します。

import defusedxml.ElementTree as ET

def print_element_name(element, level=0):
    prefix_str = '\t' * level
    result_list.append(prefix_str + element.tag)
    for sub in element:
        print_element_name(sub, level=level+1)

result_list = []
tree = ET.parse('test.xml')
root = tree.getroot()
for sub in root.iter(root.tag):
    print_element_name(sub, level=0)

with open('out.txt', mode='wt', encoding='utf-8') as f:
    f.write('\n'.join(result_list))

再帰関数を使ってみました。

再帰的に子の要素のタグ名を取得していって、レベルに合わせた文字列を作ってリストに保管していき、最後にまとめてファイルに出力しているわけです。

出力はこうなります。

tip
     middle
             bottom
     middle
             bottom
             bottom
     middle

まあまあ見やすいですよね。

罫線で階層を表現する例

罫線を使ってみます。

import defusedxml.ElementTree as ET

def print_element_name(element, level=0):
    if level == 0:
        prefix_str = ''
    else:
        prefix_str = '│' * (level - 1) + '└'
    result_list.append(prefix_str + element.tag)
    for sub in element:
        print_element_name(sub, level=level+1)

result_list = []
#tree = ET.parse('tiddlywiki5.wordpress.2019-09-21.xml')
tree = ET.parse('test.xml')
root = tree.getroot()
for sub in root.iter(root.tag):
    print_element_name(sub, level=0)

with open('out.txt', mode='wt', encoding='utf-8') as f:
    f.write('\n'.join(result_list))

タグの前に付ける文字列を変えただけですね。

出力はこうなります。

tip
└middle
│└bottom
└middle
│└bottom
│└bottom
└middle

この例では見た目がきれいですが、最後のmiddleの子にbottomがあると左端に縦線が引かれますのでtreeの表示を完全に再現できているわけではありません。

公開日

広告