概要
Redmineのクライアントアプリ的なものを作っており、Redmineの各APIを叩いてチケット起票に必要な情報を取ってくる必要があった。RedmineのAPIレスポンスはXMLだが、パッと調べた感じjsonと違っていい感じに辞書にしてくれる機能がないっぽいので自分でやった。忘れそうなので備忘録として書いとく。もし調べ方が下手なだけでいい感じにしてくれるやつが存在するなら僕はただのバカです。
12/12追記
APIのパス末尾の.xml
を.json
にしたらふつーにjsonで返ってきました。僕はただのバカです。
まずAPIを叩く
RedmineのAPIドキュメントはこちら。この中で、とりあえずユーザ取得APIを叩きます
返ってくるデータはこんな感じ
<?xml version="1.0" encoding="UTF-8"?>
<users total_count="10" offset="0" limit="100" type="array">
<user>
<id>1</id>
<login>tanakatarou</login>
<admin>false</admin>
<firstname>太郎</firstname>
<lastname>田中</lastname>
<mail>tanakatarou@mail.com</mail>
<created_on>2021-10-10T10:00:00Z</created_on>
<last_login_on>2021-10-10T10:00:0Z</last_login_on>
</user>
<user>
<id>2</id>
<login>yamadahanako</login>
<admin>false</admin>
<firstname>花子</firstname>
<lastname>山田</lastname>
<mail>yamadahanako@mail.com</mail>
<created_on>2021-10-10T10:00:00Z</created_on>
<last_login_on>2021-10-10T10:00:0Z</last_login_on>
</user>
<users>
jsonで返ってくるならjson.loads()
で一発だが、XMLとなると使いやすい形にするのに一工夫いる。
XMLを辞書にする
今回は、ユーザIDと名前が欲しいので、以下のような形にすることを目標にする。
[
{"id":"1","name":"田中太郎"},
{"id":"2","name":"山田花子"}
]
書いた処理はこんな感じ。
import xml.etree.ElementTree as XmlET
# 1 APIを叩いて取得したXML形式の文字列をElementオブジェクトに変換(execute_api()は自作の関数)
redmine_user_xml = XmlET.fromstring(execute_api(url=redmine_user_api_url, headers=redmine_headers, method="GET"))
# 2 全てのuserタグをElementとして取得
all_user_element = redmine_user_xml.findall("user")users
# 3 userのElementからid、nameを取得しテキストにして辞書に格納
users = []
for user_element in all_user_element:
users.append({
"id": user_element.find("id").text,
"name": f"{user_element.find('lastname').text}{user_element.find('firstname').text}"
})
return users
以下、解説 (ドキュメント)
- 文字列であるXML形式のレスポンスを、pythonでパースするためにElementというオブジェクトに変換する。この変換のためにxml.etree.ElementTreeモジュールをimportする必要がある。
- 1で使ったfromstring()により、XMLのタグひとつひとつがElementオブジェクトとなり、その属性としてタグの属性や子のElementを持つような形になる。そこで、まずuserタグのElementを全て取得する。findall()を使うとタグ名で検索&取得できる。
- 「まずAPIを叩く」で取得したXMLの形式を見ると、userタグの中にidタグやfirstnameタグ、lastnameタグなどが存在しており各タグに囲まれた部分にデータ文字列が入っていることがわかる。なので、2で取得したuserのElementひとつづつに対して"id"や"firstname"、"lastname"でfindしてタグのElementを取得する。そのタグの文字列はtext属性で持っているので
.text
で取得できる。firstnameとlastnameが別れている必要はないので結合してる。
結果がこちら。
[
{"id":"1","name":"田中太郎"},
{"id":"2","name":"山田花子"}
]
いい感じ!!!
まとめ
jsonで返せ。