パラメータとしてEnumを受け取る

【前提条件】

[環境]

【概要】

前回Enumからセレクトボックスを作成しました。

今回は作成したセレクトボックスをパラメータとして受け取る方法です。
表示用にThymeleafは使用しています。

JAX-RSとThymeleafの設定は前回の記事を参照してください。

【前回から使い回すソース】

前回のソースを使いまわします。

[表示リソース]
package jp.glory.jerseymvc.thymeleaf.application.resource;

import java.util.Arrays;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.glassfish.jersey.server.mvc.Viewable;

@RequestScoped
@Path("enum")
public class EnumSample {

    @GET
    public Viewable view() {

            EnumView view = new EnumView();
            view.setCategorys(Arrays.asList(Category.values()));
            view.setSelected(Category.Tool);
            return new Viewable("/enum", view);
        }
    }
				    
[画面表示用Bean]
package jp.glory.jerseymvc.thymeleaf.application.resource;

import java.util.List;

public class EnumView {

    private List<Category> categorys = null;
    private Category selected = null;

    public List<Category> getCategorys() {
        return categorys;
    }

    public void setCategorys(List<Category> categorys) {
        this.categorys = categorys;
    }

    public Category getSelected() {
        return selected;
    }

    public void setSelected(Category selected) {
        this.selected = selected;
    }
}
[HTML]
<!DOCTYPE html>
<html>
    <head>
        <title>Enum</title>
        <meta charset="UTF-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
        <form th:action="@{/service/send}" method="post">
            <select name="category">
                <option th:each="category : ${item.categorys}"
                        th:selected="${item.selected.id == category.id}"
                        th:value="${category.id}"
                        th:text="${category.label}">hoge</option>
            </select>
            <button>送信</button>
        </form>
    </body>
</html>

HTML部分については前回から変更があります。
selectタグにname属性を付与しました。

Enum

変換対象となるEnumです。

package jp.glory.jerseymvc.thymeleaf.application.resource;

import java.util.Arrays;

public enum Category {

    ProgrammingLanguage(1, "プログラム言語"),
    Server(2, "サーバ"),
    Tool(3, "ツール");

    private final int id;
    private final String label;

    private Category(int id, String label) {

        this.id = id;
        this.label = label;
    }

    public int getId() {
        return id;
    }

    public String getLabel() {
        return label;
    }

    public static Category fromString(String paramValue) {

        int value = Integer.parseInt(paramValue);

        return Arrays.stream(Category.values())
                     .filter(v -> v.getId() == value)
                     .findFirst()
                     .get();
    }
}

idとlabelを持つEnumです。
ここら辺は前回の記事と変わりないです。

違うのはfromStringというメソッドが増えていることです。
fromStringというString型のパラメータを一つ持つstaticなメソッドがあると
JAX-RSでは自動で変換してくれる仕様です。

ここら辺は仕様書の「3.2 Fields and Bean Properties」に書かれています。
以下、仕様からの引用です。

[JAX-RS 仕様書より]

4. Types that have a static method named valueOf or fromString with a single String argument
that return an instance of the type. If both methods are present then valueOf MUST be used unless
the type is an enum in which case fromString MUST be used.

メソッド名はvalueOfでも良いそうです。
ただ、Enumの場合はデフォルトでvalueOfメソッドが定義されているため、
valueOfでは変換ができない場合はfromStringメソッドを使うことになります。

今回のサンプルではidをキーにして取得しているので、valueOfでは対応できないパターンです。

【実際に動かしてみる】

ここからは実際に動かすソースになります。

[表示リソースクラス]
package jp.glory.jerseymvc.thymeleaf.application.resource;

import javax.enterprise.context.RequestScoped;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import org.glassfish.jersey.server.mvc.Viewable;

@RequestScoped
@Path("send")
public class SendEnumSample {

    @POST
    public Viewable output(@FormParam("category") Category category) {

        EnumView view = new EnumView();
        view.setSelected(category);

        return new Viewable("/send", view);
    }
}

FormParamアノテーションを使って、Categoryクラスの値をパラメータとして受け取ります。
受け取ったパラメータを表示用のBeanに設定して、Viewableを生成して返却しています。

[表示用HTML]
<!DOCTYPE html>
<html>
    <head>
        <title>Enum</title>
        <meta charset="UTF-8" />
	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
    </head>
    <body>
         <p>あなたが選んだのは[<span th:text="${item.selected.label}"></span>]です。</p>
    </body>
</html>

リソースクラスで設定した内容を表示しています。

[表示結果]

前回から使いますのところで書いたHTMLをブラウザで表示して、
画面のセレクトボックスから[サーバ]を選択して[送信]ボタンをクリックします。

そうすると下のような画面が表示されます。

送信ボタンがある画面からパラメータが渡されて、
JAX-RSがちゃんと変換していることがわかりますね。

【まとめ】

JAX-RSでは難しい設定をせずにEnumへの変換ができました。
今回のサンプルではEnumを例にしましたが、
valueOf/fromStringメソッドさえ作れば普通のクラスでも変換できそうですね。
(そこらへんは試していませんが・・・・)

メソッド名がvalueOf/fromStringメソッドと固定されてしまうので、
アノテーションのほうがよかったんじゃないかなぁとは個人的に思ったり。

メソッド名が固定であるのはそれはそれでメリットなので、
現状の仕様で不便は感じないと思いますが。

結論としては
JAX-RSではEnumでのやり取りが簡単なので、
Enumを使いたい場面では迷わず使ってしまって良さそうですね。