http://xmlstar.sourceforge.net/
コマンドラインで XML を操作できるツール。-t が便利。
ごく稀に XML を処理して加工してなにかにしたいことがあると思う。今回の場合、雑に FindBugs の結果を ignore したかったのだが、そこそこ数があってめんどい。
SpotBugs(FindBugs) の結果 XML から ignore filter を雑に出力する
SpotBugs の report XML は以下のような感じ。ほとんどはしょっている。
<BugCollection ...>
<BugInstance type="...">
...
</BugInstance>
</BugCollection> これを
<Match> <Bug pattern="..."/> <Class name="..."/> </Match>
という filter のルールに変換したい。XML to XML なので XSLT で出番だ!! しかし XML は書きたくない!!!!
xmlstarlet ではこれは以下のようにできる。ようは XSLT のテンプレートをコマンドライン引数からつくってるだけなのだけど、これがなかなか便利。
xmlstarlet select --indent \ -t -m '//BugInstance' \ -n \ -e 'Match' \ -e 'Bug' \ -a 'pattern' -v '@type' \ -b \ -b \ -e 'Class' \ -a 'name' -v './Class/@classname' \ build/reports/spotbugs/*.xml
-t の説明を読めばなにが起こっているかすぐわかるでしょう。
やっていることは
- //BugInstance にマッチさせてそれぞれに対して
- Match 要素を生成
- Bug 要素を生成
- Bug 要素に pattern 属性を生成
- code 属性の値を ./@type (BugInstance の type 属性) から生成
- "-b" で pattern 属性のネストを抜ける
- "-b" で Bug 要素のネストを抜ける
- Class 要素を生成
- Class 要素に name 属性を生成
- name 属性の値を ./Class/@classname から生成
- (自動的にネストは閉じられる)
まぁ XSLT を簡単に書けるというだけなので、XSLT の知識なしでうまく書けるかというと疑問がある。(僕はアホみたいにXSLT書いてた時期があるので気持ちよく書けます!!)
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:exslt="http://exslt.org/common" version="1.0" extension-element-prefixes="exslt">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:template match="/">
<xsl:for-each select="//BugInstance">
<xsl:value-of select="'&#10;'"/>
<xsl:element name="Match">
<xsl:element name="Bug">
<xsl:attribute name="pattern">
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="@type"/>
</xsl:call-template>
</xsl:attribute>
</xsl:element>
<xsl:element name="Class">
<xsl:attribute name="name">
<xsl:call-template name="value-of-template">
<xsl:with-param name="select" select="./Class/@classname"/>
</xsl:call-template>
</xsl:attribute>
</xsl:element>
</xsl:element>
</xsl:for-each>
</xsl:template>
<xsl:template name="value-of-template">
<xsl:param name="select"/>
<xsl:value-of select="$select"/>
<xsl:for-each select="exslt:node-set($select)[position()&gt;1]">
<xsl:value-of select="'&#10;'"/>
<xsl:value-of select="."/>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet> この XSLT テンプレートと同様のことになる。xmlstarlet のコマンドラインのほうがはるかに書くのは楽ですね。