Struts2の自動型変換(2014 summer version)

# 自動型変換とは

Struts2には、画面からのリクエストパラメータに対し、自動的にString以外の型へ自動変換する仕組みがあります。これをTypeConvertionと呼びます。

## デフォルトで自動型変換を行ってくれる型

自動型変換は、プリミティブ型、配列、java.util.ListなどがStruts2内部で実装済みです。

## それ以外にも自動型変換したいけどできる？

Javaのクラスであればすべて可能です。手順は次の通りです。

+ Struts2が提供するStrutsTypeConverterを継承したクラスを作る
+ 型変換するよ！と宣言する

## 例えばBigDecimalをやってみよう

まず、TypeConverterクラスを継承して、BigDecimal用の型変換クラス、BigDecimalConvertoerを作ります。
継承して、以下の2メソッドを実装していきます。

```java:BigDecimalConverter
public class BigDecimalConverter extends StrutsTypeConverter {
  public Object convertFromString(
    Map context,
    String[] values,
    Class toClass) {
    // 画面→Actionクラス変換
  }

  public String convertToString(Map paramMap, Object paramObject) {
    // 画面←Actionクラス変換（正常系のみ）
  }
}
```

## 変換の設定はどこで？

Struts2では以下の2か所で設定できます。どちらかを選びましょう。

+ 特定の型に対してすべて定義する : xwork-conversion.properties
+ Actionクラスごとに定義する : [Actionクラス名]-conversion.properties

ちなみに、Actionクラスごとに定義する方法として、プロパティファイル以外にはアノテーションでも設定可能です。

次の例は、BigDecimal型はすべて変換する例です。

```properties:xwork-conversion.properties
# BigDecimal
java.math.BigDecimal=lumi2.converter.BigDecimalConverter
```

## 型変換エラーを起こした時は？

作成したTypeConverterクラスのconvertFromStringメソッドにて、次のメソッドを呼ぶと、Struts2内部では「この項目は型変換が失敗したので表示用に元の値を表示する項目」になります。

```java:BigDecimalTypeConverter.java
super.performFallbackConversion(context, paramArrayOfString, toClass);
```

## 実装サンプル(BigDecimalConverter)

では実際の実装例です。複数同じ名前のパラメータがリクエストされても対応できるよう、配列チェックをしてから変換しています。

ついでに、カンマ編集をこのクラスで行っています。

```java:BigDecimalTypeConverter.java
public class BigDecimalConverter extends StrutsTypeConverter {

	/**
	 * リクエストパラメータからの置換を行う。
	 * @param Map コンテキスト情報Map
	 * @param String[] パラメータの配列
	 * @param Class 変換後のクラス
	 * @return 型変換後のパラメータ（オブジェクト）
	 */
	@Override
	public Object convertFromString(
			Map context, String[] values, Class toClass) {
		Object result = null;

		log.debug("convertFromString -> " + context + ", "
				+ values.toString() + ", " + toClass.getName());

		if (values != null) {
			if (values.getClass().isArray() && toClass.isArray()) {
				Class<?> componentType = toClass.getComponentType();

				result = Array.newInstance(componentType,
						Array.getLength(values));
				for (int i = 0, icount = Array.getLength(values); i < icount; i++) {
					Array.set(result, i,
							convertBigDecimalValue(
									context ,
									Array.get(values, i) ,
									toClass
							)
					);
				}
			} else {
				// カンマを取り除く
				result = convertBigDecimalValue(context , values[0] , toClass);
			}
		}
		return result;
	}

	/**
	 * BigDecimalへの型変換。
	 *
	 * @param Map コンテキスト情報Map
	 * @param Object 変換対象の文字列
	 * @param Class 変換後のクラス
	 * @return 型変換した後のオブジェクト
	 */
	protected Object convertBigDecimalValue(
			Map context ,Object paramArrayOfString ,Class toClass
			) {
		BigDecimal result;

		String value = (String)paramArrayOfString;
		value = value.replace(",", "");
		try {
			// Struts2標準で持っているBigDecimal変換
			result = bigDecValue(value);
		} catch (NumberFormatException e) {
			// 型変換エラーをStruts2へ通知する = JSPは変換前を表示
			super.performFallbackConversion(context, paramArrayOfString, toClass);

			return value;
		}
		return result;
	}

	/**
	 * BigDecimalからStringへの変換を行う。
	 * @param paramMap コンテキスト情報Map
	 * @param paramObject Actionクラスのフィールド
	 * @return 画面に表示する文字列
	 */
	public String convertToString(Map paramMap, Object paramObject) {
		if ( paramObject instanceof BigDecimal ) {
			NumberFormat fmt = NumberFormat.getInstance();
			BigDecimal dec = new BigDecimal(paramObject.toString());
			return fmt.format(dec);
		} else {
			return paramObject.toString();
		}
	}
}

```

## アノテーションで設定する例、Actionクラスごとに設定する例は？

長くなるので分けます(´・ω・`)