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 のコマンドラインのほうがはるかに書くのは楽ですね。

  1. トップ
  2. tech
  3. XML をコマンドラインからクエリー (XSLT) したいときに使える xmlstarlet