WEBアプリケーションにおいて、サーバでCSVを作成して、クライアント(ブラウザ)にダウンロードする、といった要件は頻繁に発生します。
SpringMVCでXSLファイルを上手に使えば、簡単にCSVファイルの作成とダウンロードの機能を作成することができます。
今回は、SpringMVCでCSV出力をおこなう方法を紹介します。
Javaで自力でゴリゴリ作成する方法ではなく、XSLファイルを使ってCSV出力おこなう方法となります。
XSLファイルとは、XMLファイルのスタイルを定義しているファイルになります。
出力するCSVファイルのレイアウトをXSLファイルに定義してあらかじめサーバ上に格納しておき、CSVファイルを出力する際に使用します。
XSLファイルとは何か?
XSL(eXtensible Style Language)ファイルは、XML 文書を他の文書タイプに変換したり、出力をフォーマットする際に使用できるスタイルシートです。
Styleという言葉が使われていることからもわかるように、XMLのスタイルシートがXSLになります。
このXSLファイルで便利なのが、テンプレート関数というちょっとしたロジックをXSLファイル内で使用することができる点です。
例えば、以下のような関数です。
- テンプレート関数の定義の仕方(xsl:template、xsl:param)
- 引数の値の参照の仕方($引数名)
- 条件分けの仕方(xsl:if、xsl:choose)
- XSLT関数(not、contains、substring-before、substring-after)
- XSLT関数の使い方(<xsl:value-of select=”XSLT関数 (引数…)” />)
- 定義されたテンプレート関数の呼び出し方(xsl:call-template、xsl:with-param:再帰呼び出しの箇所)
上記のような関数もあらかじめXSLファイル内に定義することができます。
以下に、よく使う関数を紹介していきます。
文字列の連結
concat(str1,str2,str3,・・・)
str1、str2、str3、を連結
文字列の調査
contains(str, substr)
strの中のsubstrを検索。
存在する場合はtrueを通知
数字のフォーマッティング
format-number(number, format)
format-number(number, format, formatType)
numberの数字を、formatで指定されたフォーマットに変換して出力します。
空白の除去
normalize-space(str)
strから空白を除外した文字列を返却します
先頭文字のチェック
starts-with(str, substr)
strがsubstrで始まっているかをチェックします。
始まっている場合はtrueを通知。
文字列への変換
string(val)
valを文字列に変換します。
文字列の長さを取得
string-length(str)
strの長友を取得します。
文字列の抜き出し
substring(str, start)
substring(str, start, length)
strに対して、start位置からlengthの長さの文字列を取得します。
文字列の抜き出し(前文字列の取得)
substring-before(str, substr)
strからsubstrの文字列を検索し、見つかったらその前の文字列を取得します。
substring-after(後文字列の取得)
substring-after(str, substr)
strからsubstrの文字列を検索し、見つかったらその後の文字列を取得します。
文字列の置換
translate(str, src, dest)
strに含まれるsrcという文字列をdestに変換します。
切り上げ
ceiling(val)
valの切り上げをおこないます。
ノード数の取得
count(node)
XML内に含まれるnodeで指定されたノードの数を取得します。
<a> <b></b> <b></b> </a>
この例で、’b’のノード数は2です。
切り下げ
floor(val)
valの切り下げをおこないます。
数値に変換
number(any)
anyを数値に変換します。
四捨五入
round(val)
valを四捨五入します。
加算
sum(node)
nodeで指定されている値を加算し、数値で返却します。
<a> <b>10</b> <b>10</b> </a>
sum(‘b’)の返却値は20です。
現在のノード取得
current()
現在位置のノードを取得します。
現在のノードの最下層ノードを取得
last()
nodeのローカル名(名前空間を外したもの)を取得
local-name(node)
現在のノード名を取得
name()
現在のノードの名前空間URIを取得
namespace-uri()
現在のノード位置を数値で取得
position()
boolean型への変換
boolean(any)
anyをboolean(true or false)に変換します。
falseの出力
false()
boolean型のfalseを出力します。
trueの出力
true()
boolean型のtrueを出力します。
否定
not(boolean)
booleanの否定値をbooleanで出力します。
ドキュメントルートの取得
document(uri)
uriのドキュメントルートを出力します。
xsltで解釈できるxlstノードかを判定
element-available(str)
返り値はbooleanです。
xsltで解釈できる関数かを判定
function-available(func)
ノードに固有の値を割り当て
generate-id(node)
該当するidのノードを取得
id(any)
キー値を返却
key(str any)
str内のハッシュに、anyで指定される表現を返却します。
XSLT固有値の取得
system-property(str)
str内のXSLT固有の値を取得します。
<!entity>タグ値を取得
unparsed-entity-uri()
<!entity>タグで指定されている値を取得します。
CSV出力のサンプルソース
CSV出力のサンプルソースを紹介します。
SpringMVCのコントローラクラスに実装することを想定しています。
@RequestMapping(params = "csvOut", produces = "text/csv") public ModelAndView makeCsvOut(HttpServletResponse response, Form form) throws JAXBException, IOException { // DBから出力する情報を取得してDTOに格納 CsvOutDTO dto = this.service.geCsvInfo(form); // DTOをXMLの形式に変換 JAXBContext context = JAXBContext.newInstance(CsvOutDTO.class); Writer writer = new StringWriter(); context.createMarshaller().marshal(dto, writer); // モデルに変換したXMLを格納 String xslName = "CsxXsl"; ModelAndView modelAndView = new ModelAndView(xslName); modelAndView.addObject( "xmlSource", new StringReader(writer.toString())); // HTTPレスポンスヘッダを作成 response.setHeader("Content-Disposition", "attachment; filename=csvFile.csv, "UTF-8")); // BOMを先頭に付加 response.getOutputStream().write(CxFix.BOM); return modelAndView; }
データベースからCSV出力する内容を取得することを想定しています。
「geCsvInfo」の中身は消略していますが、このメソッドの中身がDBアクセスしてDTOのインスタンスを返却するイメージです。
その後はサンプルの通りで、DTOを「JAXBContext」に渡せば、CSVファイルの出来上がり。
次にXSLファイルのサンプルです。
上記のJavaソースで”CsvXsl”という名前で定義されたXSLファイルのサンプルになります。
<?xml version="1.0" encoding="UTF-8" ?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns="http://www.w3.org/1999/xhtml"> <xsl:output method="text" omit-xml-declaration="yes" byte-order-mark="yes" encoding="UTF-8" /> <xsl:template match="/csvList"> <xsl:call-template name="template_csvList" /> </xsl:template> <xsl:template name="template_csvList"> <!-- ヘッダ情報 --> <xsl:text>カラム1,</xsl:text> <xsl:text>カラム2,</xsl:text> <xsl:text>カラム3,</xsl:text> <xsl:text> </xsl:text> <!-- データ情報 --> <xsl:value-of select="translate(translate(translate( ./entity/col1, '"', ' '), ',', ' '), '', ' ')" /> <xsl:text>,</xsl:text> <xsl:value-of select="translate(translate(translate( ./entity/col2, '"', ' '), ',', ' '), '', ' ')" /> <xsl:text>,</xsl:text> <xsl:value-of select="translate( translate(translate(./entity/col3, '"', ' '), ',', ' '), '', ' ')" /> <xsl:text>,</xsl:text> </xsl:template> </xsl:stylesheet>
「entity」とは、前のJavaコードに記載していた、「CsvOutDTO」で使用しているエンティティクラスです。
1行データ毎に1エンティティのレコードを保持します。
CSVファイルは1行目は各項目名の説明で、実際のデータ出力は2行目からです。
XSLファイルで2行目以降を動的にすることで、「CsvOutDTO」に格納されているエンティティの数だけCSVデータ出力がおこなわれます。
関数は「translate」しか使っていません。
ダブルクオテーションと改行コードを半角スペースに変換しています。
まとめ
いかがでしたでしょうか?
SpringMVCでCSV出力をおこなう場合に、参考にしてもらえればと思います。
特に、XSLの関数は助かります。
ちょっとした処理をJavaでおこなう必要がないので。
それではまた!