BeanUtils で JavaBeans のプロパティを一気にコピーする で作成したサンプルでは、 java.util.Date を String にコピーした時に “yyyy/MM/dd HH:mm:ss” というような任意のフォーマットへの変換は出来ませんでした。 これは BeanUtils のデフォルトの振る舞いが java.util.Date#toString() の値を使用して String へ変換するようになっている為です。 まずその仕組みを少し調べ、独自の変換を行うにはどうすれば良いかを試してみます。
BeanUtils が型の違うプロパティを変換する仕組み
BeanUtils ではコピー元とコピー先のオブジェクトを比較して、 同じ名前のプロパティがあれば、 その型が違っても自動で変換してくれます。 例えば下図において、 コピー元の UserModel#getLoginDate() が java.util.Date を返すのに対し、 コピー先の UserForm#setLoginDate(String) は String を設定するメソッドになっています。
このように型が違うものを自動変換する仕組みとして、 BeanUtils は Converter インターフェースを用意しています。 デフォルトで表1のコンバーターが登録されているため、 プリミティブ型や基本クラスに関しては自動変換できるようになっています。
コピー先プロパティの型 | 導入Ver. | 対応する(※1)コンバーター(実際には1行) | デフォルトの変換内容(BeanUtils 1.7) | ||
---|---|---|---|---|---|
コピー元の値が… | |||||
null | 同じ型 | その他 | |||
boolean | 1.3 | Boolean Converter |
false | そのまま | <元の値>.toString() が “yes”, “y”, “true”, “on”, “1” ならばtrue, “no”, “n”, “false”, “off”, “0” ならば false (大文字小文字は区別しない) |
Boolean | 同上 | Boolean.FALSE | 同上 | ||
char | 1.3 | Character Converter |
半角スペース | そのまま |
<元の値>.toString().charAt(0) 例外発生時は null の場合と同じ。 |
Charcter | 同上 | new Charcter(‘ ‘) | 同上 | ||
String | 1.3 | String Converter |
null | – | <元の値>.toString() |
byte | 1.3 | Byte Converter |
0 | そのまま |
元の値が Number のサブクラス(※2)
ならば、((Number)<元の値>).byteValue() それ以外は new Byte(<元の値>.toString()) で例外発生時は null の場合と同じ。 |
Byte | 同上 | new Byte((byte)0) | 同上 | ||
short | 1.3 | Short Converter |
0 | そのまま |
元の値が Number のサブクラス(※2)
ならば、((Number)<元の値>).shortValue() それ以外は new Short(<元の値>.toString()) で例外発生時は null の場合と同じ。 |
Short | 同上 | new Short(0) | 同上 | ||
int | 1.3 | Integer Converter |
0 | そのまま |
元の値が Number のサブクラス(※2)
ならば、((Number)<元の値>).intValue() それ以外は new Integer(<元の値>.toString()) で例外発生時は null の場合と同じ。 |
Integer | 同上 | new Integer(0) | 同上 | ||
long | 1.3 | Long Converter |
0 | そのまま |
元の値が Number のサブクラス(※2)
ならば、((Number)<元の値>).longValue() それ以外は new Long(<元の値>.toString()) で例外発生時は null の場合と同じ。 |
Long | 同上 | new Long(0) | 同上 | ||
float | 1.3 | Float Converter |
0.0 | そのまま |
元の値が Number のサブクラス(※2)
ならば、((Number)<元の値>).floatValue() それ以外は new Float(<元の値>.toString()) で例外発生時は null の場合と同じ。 |
Float | 同上 | new Float((float)0.0) | 同上 | ||
double | 1.3 | Double Converter |
0.0 | そのまま |
元の値が Number のサブクラス(※2)
ならば、((Number)<元の値>).doubleValue() それ以外は new Double(<元の値>.toString()) で例外発生時は null の場合と同じ。 |
Double | 同上 | new Double((double)0.0) | 同上 | ||
boolean[] | 1.4 | BooleanArray Converter |
new boolean[0] | そのまま | boolean と同じルールで変換された boolean 配列 |
char[] | 1.4 | CharacterArray Converter |
new char[0] | そのまま | char と同じルールで変換された char 配列。例外発生時は null の場合と同じ。 |
String[] | 1.4 | StringArray Converter |
new String[0] | そのまま |
元の値が int 配列ならば、Integer.toString(<元の値の各要素>) して作成した配列。 それ以外は <元の値の各要素>.toString()) して作成した配列で、例外発生時は null の場合と同じ。 |
byte[] | 1.4 | ByteArray Converter |
new byte[0] | そのまま | Byte.parseByte(<元の値の各要素>.toString()) して作成した配列。 例外発生時は null の場合と同じ。 |
short[] | 1.4 | ShortArray Converter |
new short[0] | そのまま | Short.parseShort(<元の値の各要素>.toString()) して作成した配列。 例外発生時は null の場合と同じ。 |
int[] | 1.4 | IntegerArray Converter |
new int[0] | そのまま | Integer.parseInt(<元の値の各要素>.toString()) して作成した配列。 例外発生時は null の場合と同じ。 |
long[] | 1.4 | LongArray Converter |
new long[0] | そのまま | Long.parseLong(<元の値の各要素>.toString()) して作成した配列。 例外発生時は null の場合と同じ。 |
float[] | 1.4 | FloatArray Converter |
new float[0] | そのまま | Float.parseFloat(<元の値の各要素>.toString()) して作成した配列。 例外発生時は null の場合と同じ。 |
double[] | 1.4 | DoubleArray Converter |
new double[0] | そのまま | Double.parseDouble(<元の値の各要素>.toString()) して作成した配列。 例外発生時は null の場合と同じ。 |
BigDecimal | 1.3 | BigDecimal Converter |
例外発生(※3) | そのまま | new BigDecimal(<元の値>.toString()) |
BigInteger | 1.3 | BigInteger Converter |
例外発生(※3) | そのまま | new BigInteger(<元の値>.toString()) |
java.sql. Date |
1.3 | SqlDate Converter |
例外発生(※3) | そのまま | java.sql.Date#valueOf(<元の値>.toString())。例外発生時は null の場合と同じ。 |
java.sql. Time |
1.3 | SqlTime Converter |
例外発生(※3) | そのまま | java.sql.Time#valueOf(<元の値>.toString())。例外発生時は null の場合と同じ。 |
java.sql. Timestamp |
1.3 | SqlTimestamp Converter |
例外発生(※3) | そのまま | java.sql.Timestamp#valueOf(<元の値>.toString())。例外発生時は null の場合と同じ。 |
Class | 1.4 | Class Converter |
例外発生(※3) | そのまま | 現在のスレッドのコンテキストクラスローダ、または ClassConverter クラスのクラスローダから ClassLoader#loadClass(<元の値>.toString()) した値。例外発生時は null の場合と同じ。 |
java.io. File |
1.7 | File Converter |
例外発生(※3) | そのまま | new File(<元の値>.toString()) |
java.net. URL |
1.7 | URL Converter |
例外発生(※3) | そのまま | new URL(<元の値>.toString())。例外発生時は null の場合と同じ。 |
※2 java.lang.Number のサブクラスは BigDecimal,BigInteger,Byte,Double,Float,Integer,Long,Short
※3 投げられる例外は org.apache.commons.beanutils.ConversionException
BeanUtils でコンバーターが使用される手順
では、 java.util.Date 型 → String 型 と変換する際、 BeanUtils#copyProperties(Object, Object) ではどのような処理を行っているのでしょうか? 前述の loginDate というプロパティをコピーする際の処理を下記に示します。 (下の例では実際の処理内容を単純化しています。)
- UserModel(コピー元)#getLoginDate() の値を取得
- UserForm(コピー先)の loginDate という名前のプロパティに関する PropertyDescripter を取得
- 取得した PropertyDescripter で、コピー先プロパティの型(この場合は String)を取得
- 取得した型に対応するコンバーターを ConvertUtils から取得(この場合は StringConverter)
- Converter#convert(Class, Object) メソッドを使用して 1 で取得した値を変換
- 変換済みの値を UserForm(コピー先)の setter メソッドを使用して設定
つまり、 UserModel の loginDate は Date 型ですが、これを変換する際には、 UserForm の loginDate が String である為、 あらかじめ ConvertUtils に登録されている StringConverter が使用されています。 StringConverter の実装はというと…
public final class StringConverter implements Converter { public Object convert(Class type, Object value) { if (value == null) { return ((String) null); } else { return (value.toString()); } } }
…となっています。 第1引数の type には、この場合、常に String.class という値が渡されます。 第2引数の value が変換前の値で、 実際に UserModel#getLoginDate() で取得した java.util.Date 型の値が渡されます。 これで BeanUtils#copyProperties(Object, Object) を使用した際に java.util.Date#toString() の値がコピー先に設定された理由が分かりました。
独自のコンバーターを作成・登録
BeanUtils がコンバーターを使用している仕組みが分かったところで、 loginDate を “yyyy/MM/dd HH:mm:ss” 形式で変換できるように、 独自のコンバーターを作成してみます。
public final class CustomStringConverter implements Converter { public Object convert(Class type, Object value) { if (value == null) { return ((String) null); } else { if (value instanceof java.util.Date) { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); return sdf.format((java.util.Date)value); } return (value.toString()); } } }このコンバータを使用するには、 ConvertUtils#register(Converter, Class) メソッドを使用します。
-+-+-+-+-+-+-+-+-+-+- Main.java -+-+-+-+-+-+-+-+-+-+- package jp.javable.sample.beanutils; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.beanutils.ConvertUtils; public class Main { public static void main(String[] args) throws Exception { ConvertUtils.register(new CustomStringConverter(), String.class); UserModel model = new UserModel(); model.setName("溝口"); model.setLoginDate(new java.util.Date()); UserForm form = new UserForm(); BeanUtils.copyProperties(form, model); System.out.println(form); } }
実行してみます。
D:\eclipse_workspace\test> java -classpath bin;lib\commons-beanutils.jar;
lib\commons-collections.jar;
lib\commons-logging.jar jp.javable.sample.beanutils.Main
(↑実際には1行です)
jp.javable.sample.beanutils.UserForm@145d068[name=溝口,
loginDate=2005/05/20 23:24:12]
ちゃんとフォーマットされていました(T^T)。[
サンプルソース
]