Kotlin API を叩く際に Enum を JSON から変換する
- SpringBoot2
- Thymeleaf
- Jackson (Jsonのシリアライズ・デシリアライズで利用)
- パラメータで渡すのが一番簡単ですが、折角(?)なので JSON で渡してみる
- Jsonで渡すと書いてありますが実際は、Json -> Enum 変換
- Kotlin
実施環境
開発PC: Windows 10
Java: 8
Kotlin: 0.8.19
Eclipse: 2019-06 (4.12.0)
想定される場面
- 連動するセレクトボックスなど
- 1つ目のセレクトボックスの選択によって2つ目のセレクトボックスの内容が変わる場合
- Enum の入れ子でやってみる
- Enum は継承出来ないのが今ひとつ
1つ目のセレクトボックス用 Enum
- JsonSerialize, JsonDeserialize アノテーションでシリアライズ・デシリアライズのクラスを指定
@JsonSerialize(using = EnumSerializer::class)
@JsonDeserialize(using = EnumDeserializer::class)
enum class Select1Enum(val text:String, val arr:Array<*>) {
SELECT1("セレクト2-1", Select21Enum.values()),
SELECT2("セレクト2-2", Select22Enum.values()),
SELECT3("セレクト2-3", Select23Enum.values()),
;
// 名称により Enum 取得
fun getByText(text: String):Select1Enum? {
var result: Select1Enum? = null
enumValues<Select1Enum>().forEach {
if (it.text == text) {
result = it
}
}
return result
}
}
override fun toString():String {
return ReflectionToStringBuilder.toString(this)
}
}
2つめのセレクトボックス用 Enum
- 同様に複数用意
enum class Select21Enum(val text:String) {
SELECT1("セレクト2-1ー1"),
SELECT2("セレクト2-1-2"),
SELECT3("セレクト2-1-3"),
;
// 名称により Enum 取得
fun getByText(text: String):Select21Enum? {
var result: Select21Enum? = null
enumValues<Select21Enum>().forEach {
if (it.text == text) {
result = it
}
}
return result
}
}
}
シリアライズ・デシリアライズ用のクラス
- 簡単に Enum の text で enum の項目が指定できるように
class EnumDeserializer:JsonDeserializer<Any>() {
@Throws(IOException::class)
override fun deserialize(jp:JsonParser, ctxt:DeserializationContext):Select1Enum? {
val jsonNode:JsonNode = jp.getCodec().readTree(jp)
val jsonValue = jsonNode.get("text").asText()
for (enumValue in Select1Enum.values()) {
if (enumValue.text.equals(jsonValue)) {
return enumValue
}
}
return null
}
}
class EnumSerializer:JsonSerializer<Select1Enum>() {
@Throws(IOException::class)
override fun serialize(value:Select1Enum, jgen:JsonGenerator, provider:SerializerProvider) {
jgen.writeStartObject()
jgen.writeFieldName("text")
jgen.writeString(value.text)
jgen.writeEndObject()
}
}
APIで出力するテンプレートを用意
- fragment_select2.html(thymeleaf)
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8"/>
<title>Insert title here</title>
</head>
<body id="fragmentBody" th:fragment="fragmentBody()">
<select name="select2" id="select2" th:fragment="fragmentSelect()">
<option value="">選択してください。</option>
<option th:if="${select2List != null}" th:each="i : ${select2List}" th:value="${i.text}" th:text="${i.text}" th:selected="${i.text} == *{select2}"> --- </option>
</select>
<span th:if="${#fields.hasErrors('select2')}" th:errors="*{select2}" id="select2.errors" class="help-block error" th:style="|display: block;|">正しく入力してください。</span>
</body>
</html>
API出力用コントローラ
- /api/select/getSelect2
- @RequestBody アノテーションをつける
- 後述 javascript で呼び出される
- Json 形式で受け取ったパラメータを Enum へデシリアライズ
@Controller
@RequestMapping("api/select")
open class SelectApiController {
@PostMapping(path=["/getSelect2"],
consumes=[MediaType.APPLICATION_JSON_VALUE])
open fun getSelect2(@RequestBody(required=false) select1:Select1Enum?, model:Model): String {
if (select1 != null) {
model.addAttribute("select2List", select1.arr)
}
return "fragment_select2::fragmentSelect()"
}
}
入力用フォームのHTML(抜き出し)
<div>
<select name="select1" id="select1" th:field="*{select1}">
<option value="">選択してください。</option>
<option th:each="i : ${selectList}" th:value="${i.text}" th:text="${i.text}">選択1</option>
</select>
<span th:if="${#fields.hasErrors('select1')}" th:errors="*{select1}" id="select1.errors" class="help-block error" th:style="|display: block;|">セレクト1を正しく入力してください。</span>
</div>
<div id="fragment_select2" th:include="select/fragment_select2::fragmentBody()">
セレクトボックス連動用 javascript
- jquery 利用
- Json 形式で渡す
- 結果は #fragment_select2 に注入
// ページロード後に呼び出す
function initializeSelect1Events() {
// 業種セレクト連動
$("#select1").on('change', function(e) {
select1OnChange();
});
}
function select1OnChange() {
var url = contextPath + '/api/select/getSelect2';
var value = $("#select option:selected").val();
var data = JSON.stringify({'text': value});
$.ajax({
headers: {
'Content-Type': 'application/json'
},
url: url,
type: 'POST',
cache: false,
dataType: 'html',
data: data,
traditional: true,
beforeSend: function(xhr, settings) {}
})
.done(function(data, textStatus, xhr) {
if (data.error !== undefined) {
alert(data.error);
}
else if (data.length == 0) {
alert("セレクト2が取得できませんでした");
}
else {
$("#fragment_select2").html(data);
}
})
.fail(function(xhr, textStatus, errorThrown){
alert("セレクト2が取得できませんでした");
})
.always(function(xhr, msg){
});
}
以上、お疲れさまでした!