2019年12月23日月曜日

Motionbuilder:python:ElementTree:MotionbuilderでXMLを扱ってみる

XMLからデータを取得してあーだこーだしなければならなくなったのでElementTreeを使ってみた。
文字コード関係とかどうも拾ってきたサンプルがうまく動かないことが多かったので、

Motionbuilderで使ってみたという前提

で書く。
マニュアルはここ

実行すると以下のXMLファイルが作られる。
(コマンドログの一番上のフォルダにtest.xmlで出力されている)
import xml.etree.ElementTree as ET
XMLを扱うにはlxmlとxml.etree.elementtreeというライブラリがあるっぽいが、導入が簡単だったのでxml.etree.elementtreeを使ってみた。
違いはXMLの整形機能の内容くらいらしいが、xml.etree.elementtreeにはXML整形出力機能が弱い。
導入できるならlxmlを使った方がいいかもしれない。サンプルも微妙にlxmlの方が多い印象。

ゼロからXMLを作成

def make_xml():
あんまりやらないとは思うけど一応。

xml全体はTreeと表現される。Treeの中にElementを入れてゆく。
ビジュアル的に<tag></tag>で囲われたものがElement。


r = ET.Element('root', {'name':'rootName'})
rootとなるElementから作成開始してElementにattributeやtextや子Elementを追加してゆく。

i.text = 'あああ'.decode('utf-8') 
2バイト文字の扱いが面倒。
xml内ascii <> python内utf-8
なので取り出す時、代入する時に毎回decodeとencodeが必要。

tree = ET.ElementTree(r)
Elementの組み立てが終わったら、Treeオブジェクトを生成する。

tree.write(fl, encoding='utf-8', xml_declaration=True)
実際に.xmlファイルに出力する際に2つの方法がある。
・ElementTreeのwrite機能を使って出力する
  >簡単だが全てが一行で出力される
  >lxmlだとwrite命令に整形機能が付いているらしい
・minidomを使う
  >なんか色々面倒だけどこれを使わないと見た目が悪い。
  >writeFunction(fl, root)で後述

XMLを読み込む

def read_xml(fl):
マニュアルを見ながら適当にいじればわかると思う。
代表的な機能をピックアップした。

attributeの値はテキスト
なので、取り出した値が数値だった場合float(value)とかで変換してやらなければいけない。
代入する時も同じ。str(value)する。

print 'itemA.text.encode("utf-8"):'+itemA.text.encode('utf-8')
前述した通り2バイト文字の扱いが特殊。(普段から文字コードいじってる人には普通だと思うけど)
ここでは取り出したitemA.textをprint出力するために変換している。
取り出したままだとasciiなのでちゃんと表示されない。

XMLを編集する

def edit_xml(fl):
これもMotionbuilder経由で使っていると需要ないかも。

c = ET.SubElement(root, 'childB')
Elementを追加したり

cp = copy.deepcopy(c)
コピーしたりできる。
注意点としては追加した時に戻り値でElementが来ないので、追加前に編集しておかないと検索が面倒くさい。

XMLを保存する

def writeFunction(fl, root):
基本のwriteを使うと一行になってしまう。のと、整形出力用のwritexmlがasciiエラーを出してしまうのでちょっと面倒だった。
このあたりはMotionbuilder特有かもしれない。他の環境では要検証。

open(fl, 'w').close()
一度ファイルを全部消している。これをしないと既に構築されているインデントや改行設定が二度書きされて2回改行とかが行われてしまう。
かなり強引なので、ほかにちゃんとしたやり方があるのではないかという疑念が拭えない。
writexmlの動作が
・ファイルの中を見て
・追加部分を書き込み
・整形
という順番なので、整形済みの既存の部分がもう一度整形にかけられてしまうようだ。
ツリー全体はスクリプト内に格納済みなのでファイル側は全消去しても問題ないという判断。

with codecs.open(fl, "w", "utf-8") as out:
サンプルを見ると一つ前の行で作ったdocumentをいきなりwritexmlに入れれば動作するようだが(なにしろutf-8でストリングに変換してdocumentを作っているので)、実際にはasciiエラーを出してしまうので、writexmlに入れる前にもう一度utf-8で念押ししている。


0 コメント:

コメントを投稿