まったくnamespaceでコントロールするXMLほど扱いにくいものはありません。
ネットでも、みんなパースするのに苦労しているようです。
でも、SVGもOfficeドキュメントも、東証資料のXBRLも、どれも複数のnamespaceで構成されていて、ここから逃げることはできなさそうです。
それなら、ちゃんと向き合ってnamespaceパースをxmllintでできるようにしておきたいと思います。
Officeドキュメントのパース
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr><p:sp><p:nvSpPr><p:cNvPr id="2" name="Title 1"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph type="title"/></p:nvPr></p:nvSpPr><p:spPr/><p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="en-US" dirty="0" smtClean="0"/><a:t>{page-2:title}</a:t></a:r><a:endParaRPr lang="en-US" dirty="0"/></a:p></p:txBody></p:sp><p:sp><p:nvSpPr><p:cNvPr id="3" name="Content Placeholder 2"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph idx="1"/></p:nvPr></p:nvSpPr><p:spPr/><p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:pPr marL="0" indent="0"><a:buNone/></a:pPr><a:r><a:rPr lang="en-US" dirty="0" smtClean="0"/><a:t>{page-2:txt}</a:t></a:r><a:endParaRPr lang="en-US" dirty="0"/></a:p></p:txBody></p:sp></p:spTree><p:extLst><p:ext uri="{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}"><p14:creationId xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="2873090306"/></p:ext></p:extLst></p:cSld><p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr></p:sld>
pptxをzip解凍した時になかのスライド情報が入っているデータファイルですが、この中身の<a:t>部分にアタッチしてみたいと思います。
まず、ベースのnamespaceにアタッチするには・・・
$ xmllint --xpath "/*[local-name()='sld' and namespace-uri()='http://schemas.openxmlformats.org/presentationml/2006/main']" slide.xml
<p:sld xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:p="http://schemas.openxmlformats.org/presentationml/2006/main"><p:cSld><p:spTree><p:nvGrpSpPr><p:cNvPr id="1" name=""/><p:cNvGrpSpPr/><p:nvPr/></p:nvGrpSpPr><p:grpSpPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="0" cy="0"/><a:chOff x="0" y="0"/><a:chExt cx="0" cy="0"/></a:xfrm></p:grpSpPr><p:sp><p:nvSpPr><p:cNvPr id="2" name="Title 1"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph type="title"/></p:nvPr></p:nvSpPr><p:spPr/><p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:r><a:rPr lang="en-US" dirty="0" smtClean="0"/><a:t>{page-2:title}</a:t></a:r><a:endParaRPr lang="en-US" dirty="0"/></a:p></p:txBody></p:sp><p:sp><p:nvSpPr><p:cNvPr id="3" name="Content Placeholder 2"/><p:cNvSpPr><a:spLocks noGrp="1"/></p:cNvSpPr><p:nvPr><p:ph idx="1"/></p:nvPr></p:nvSpPr><p:spPr/><p:txBody><a:bodyPr/><a:lstStyle/><a:p><a:pPr marL="0" indent="0"><a:buNone/></a:pPr><a:r><a:rPr lang="en-US" dirty="0" smtClean="0"/><a:t>{page-2:txt}</a:t></a:r><a:endParaRPr lang="en-US" dirty="0"/></a:p></p:txBody></p:sp></p:spTree><p:extLst><p:ext uri="{BB962C8B-B14F-4D97-AF65-F5344CB8AC3E}"><p14:creationId xmlns:p14="http://schemas.microsoft.com/office/powerpoint/2010/main" val="2873090306"/></p:ext></p:extLst></p:cSld><p:clrMapOvr><a:masterClrMapping/></p:clrMapOvr></p:sld>
root階層からのデータが全て拾えました。
さらに、その個階層にあたる"a:t"へのアタッチは・・・
$ xmllint --xpath "//*[local-name()='t' and namespace-uri()='http://schemas.openxmlformats.org/drawingml/2006/main']" slide.xml
<a:t>{page-2:title}</a:t><a:t>{page-2:txt}</a:t>
非常に長いコマンドですが、それぞれのnamespaceにアタッチするたびに"local-name()"と"namespace-uri()"をセットしなければいけません。
shellオプション
xmllintはshellオプションという強力なxmlパーサー機能を持っています。
簡単に説明すると、xml内の階層を、shellコマンドのように、cd(chenge-directory)したり、内容をcatしたり、することができる言わば対話式にパースすることが出来る機能です。
通常の使い方は以下のとおりです。
$ xmllint --shell %xmlファイル
> du
内容の階層を全て表示...
> ls
同階層のリスト表示
> cat
内容テキストの表示
..
などなど
以下のようにパイプつなぎで書くこともできます。
※ヘルプの表示
$ echo "help" |xmllint --shell sample.xml
/ > base display XML base of the node
setbase URI change the XML base of the node
bye leave shell
cat [node] display node or current node
cd [path] change directory to path or to root
dir [path] dumps informations about the node (namespace, attributes, content)
du [path] show the structure of the subtree under path or the current node
exit leave shell
help display this help
free display memory usage
load [name] load a new document with name
ls [path] list contents of path or the current directory
set xml_fragment replace the current node content with the fragment parsed in context
xpath expr evaluate the XPath expression in that context and print the result
setns nsreg register a namespace to a prefix in the XPath evaluation context
format for nsreg is: prefix=[nsuri] (i.e. prefix= unsets a prefix)
setrootns register all namespace found on the root element
the default namespace if any uses 'defaultns' prefix
pwd display current working directory
whereis display absolute path of [path] or current working directory
quit leave shell
save [name] save this document to name or the original name
write [name] write the current node to the filename
validate check the document for errors
relaxng rng validate the document agaisnt the Relax-NG schemas
grep string search for a string in the subtree
ファイル名は意味はないのですが、こうすることで何ができるかがよく分かります。
そして、namepaceの対応は以下のようにすると取得できます。
$ xmllint --shell slide2.xml
/ > setns a=http://schemas.openxmlformats.org/drawingml/2006/main
/ > cat //a:t/text()
-------
{page-2:title}
-------
{page-2:txt}
でも、残念ながら、パイプで繋いでいく方式は、1つのコマンドしかechoで送ることができないので、1ライナーで行う場合は、--xpath方式で行うのが良さそうです。
とりあえず、xmlの各要素にアタッチすることができたので、ある程度の作業はできそうですね。
0 件のコメント:
コメントを投稿