はじめに
こんにちは、NEETです。最近はいろいろなデータ表現方法が出てきてるわけですが、やはりXMLっていうのは便利でしてできればデータをそのまま弄りたいってこともあるわけです。で、Webサービスとかから引っ張ってきたXMLをid順とかに要素を並べ替えたものにしたい時にlxml使うと割と簡単に出来ました。
サンプルコード
こんな感じです。何したかを簡単に書いとくと
- XML中のソートしたい対象のタグをXPathで指定してリストを引っこ抜いてくる
- 各要素中のソート基準となる要素に関する比較関数を書く
- リストのソート関数を使って並び替える
- 大元のElement Treeから並び替えた順に最後尾に差し替えていく
あとはコード読んでください。
from lxml import etree xml = """ <statuses> <status> <id>5</id> <text>spam</text> </status> <status> <id>1</id> <text>egg</text> </status> <status> <id>100</id> <text>ham</text> </status> <status> <id>2</id> <text>bacon</text> </status> </statuses> """ def sort_by_id(xml): def asc_cmp(x, y): """ 上のスキーマ決め打ちでstatusを昇順にするための比較関数。 xpathは必ずリストで返してくるので1個しかないと分かっていたら0番目の要素を指定する。 """ return int(x.xpath('./id/text()')[0]) - int(y.xpath('./id/text()')[0]) try: tree = etree.fromstring(xml) statuses = tree.xpath('//statuses') st_list = statuses[0].xpath('./status') st_list.sort(asc_cmp) for st in st_list: # ここでremove, appendする対象はtreeでもstatusesでもstatuses[0]でも結果は一緒なのに注意 tree.remove(st) tree.append(st) return etree.tostring(tree, pretty_print=True) # pretty_printにしても崩れる except Exception, e: """ 適当に処理して """ raise e if __name__ == '__main__': print xml print '--------------------' print sort_by_id(xml)
statuses[0]の中身だけゴボッと入れ替えられればforとか書かなくていいし、出力したときに綺麗になるしなんだけど、もっといいやり方ないかな。
実行結果
$ python sortxml.py <statuses> <status> <id>5</id> <text>spam</text> </status> <status> <id>1</id> <text>egg</text> </status> <status> <id>100</id> <text>ham</text> </status> <status> <id>2</id> <text>bacon</text> </status> </statuses> -------------------- <statuses> <status> <id>1</id> <text>egg</text> </status> <status> <id>2</id> <text>bacon</text> </status> <status> <id>5</id> <text>spam</text> </status> <status> <id>100</id> <text>ham</text> </status> </statuses>