LoginSignup
4
6

More than 5 years have passed since last update.

ModelMapperで転換ルール(Converter)を追加する

Posted at

Objectマッピングを簡単に行うことのできるライブラリModelMapperの転換ルール追加方法についてメモ書き程度に書いてみます。

きっかけ

JavaEEでServletを作っていた際に、POSTされてくるパラメータをBeanに自動で詰め込めないか?から調べて始め、ModelMapperに行きつきました。

発覚した問題

例えば、リクエストされたパラメータをEmployeeFormクラスにマッピングする場合、単純にHttpServletRequest#getParameterMap()を利用して取得したMapからModelMapper経由でコピーできれば良いなと、次のようなコードを書きました。

sample.java
EmployeeForm employeeForm = modelMapper.map(request.getParameterMap(), EmployeeForm.class);

しかし、HttpServletRequest#getParameterMap()に含まれている値は常にString[](文字列の配列)で格納されていて、ModelMapperをデフォルトで使うとString[]をtoStringした値が入ってきてしまいます。

Converterを使ってみる

幸い、ModelMapperは転換ルールを追加する機能があり、String[]をルールに従ってStringに入れることができます。

convert.java
// String配列→Stringに転換するルール
// 配列がnullでなければ、最初の要素を入れるようにする。
modelMapper = new ModelMapper();
Converter<String[], String> stringConverter = new AbstractConverter<String[], String>() {
    @Override
    protected String convert(String[] source) {
        return (source != null && source.length > 0) ? source[0] : "";
    }
};
// modelmapperに転換ルールを追加する。
modelMapper.addConverter(stringConverter);

※JavaEEではこれを@ApplicationScoped付きクラスの@PostConstructのメソッドに追加しておけばよいでしょう。

また、上記のルールは元情報がString[]で、受ける(コピーする)側がStringの同一名称の場合のみしか機能しませんので、チェックボックスの値などを取得した本当にListやString[]型で取得したいものについては、影響ありません。
反対に、受け取り側がStringのみのルール限定なので、Number型系(LongやBigDecimal等)、Date型などは、別途追加する必要があります。

調べていくとAbstractConverter<A, B>にはconvert(MappingContext<A, B>)というメソッドが用意されていて、うまく使って1つのルールでString[]を転換できると考えたのですが、次のような理由で断念しました。。。

できなかったこと

ModelMapperに元から含まれている転換ルールのほうが、先に適用されてしまうため、次のような汎用性の高いルールは、追加しても無視されて(デフォルトのルールのほうが優先されて)しまいました。

ダメな例
Converter<String[], Number> numConverter = new AbstractConverter<String[], Number>() {

    @Override
    public Number convert(MappingContext<String[], Number> context) {
        String[] sources = context.getSource();
        if (sources == null || sources.length <= 0) {
            return null;
        }
        String source = sources[0];
        Class<?> distType = context.getDestinationType();
        if (BigDecimal.class.equals(distType)) {
            return new BigDecimal(source);
        } else if (Long.class.equals(distType)) {
            return Long.valueOf(source);
        } else if (Integer.class.equals(distType)) {
            return Integer.valueOf(source);
        }
        return super.convert(context);
    }

    @Override
    protected Number convert(String[] source) {
        return null;
    }
};

modelMapper.addConverter(numConverter);

コンパイルとしては通るのですが、いざこれでString[] -> BigDecimalを転換しようとすると、デフォルトの転換ルールNumberConverterのほうが優先されてしまい、実行時エラーとなってしまいます。

サンプルコード、その他

以下にお試しサンプルコードを配置しています。Gradleのbuildshipを使用してください。

以下参考にしたサイト

おわりに

イメージ画像1つもない味気ない記事ですが、世間にあんまりModelMapperの入門の次の使い方とかが見受けられなかったので、備忘録レベルで残しておきました。
サンプルでお分かりいただける通り、Converterは、作ったBeanクラスだけでなく、Stringなど元々内包されているクラスに対しても転換ルールを作成することができます。便利ですね。

4
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
6