LoginSignup
1
0

More than 1 year has passed since last update.

Kotlin springでJsonからEnumへ変換する

Posted at

Jsonからenumの変換方法いろいろ

バージョン

org.springframework.boot 2.6.2

参考クラス

Controllerクラス

Controller.kt
@RequestMapping("/test")
@ResponseBody
fun test(@RequestBody json: JsonObject): String {
    println(json.toString())
    return "OK"
}

リクエストのJsonクラス

Request.kt
public data class JsonObject(
    val stringValue: String,
    val enumValue: JsonEnum
)

アノテーションなし

変換対象のenum

enum.kt
public enum class JsonEnum(
    val value: String
) {
    ENUM_TYPE_1("type_1"),
    ENUM_TYPE_2("type_2"),
    ENUM_TYPE_3("type_3");
}

enum名を表す文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "ENUM_TYPE_2"
}

リクエストの出力(Controller内で標準出力)
enumは正常に変換される。

JsonObject(stringValue=test, enumValue=ENUM_TYPE_2)

不正なenum名で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "ENUM_TYPE_0"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `sample.JsonEnum` from String "ENUM_TYPE_0": not one of the values accepted for Enum class: [ENUM_TYPE_1, ENUM_TYPE_2, ENUM_TYPE_3]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `sample.JsonEnum` from String "ENUM_TYPE_0": not one of the values accepted for Enum class: [ENUM_TYPE_1, ENUM_TYPE_2, ENUM_TYPE_3]<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

enumの0から始まる列挙順で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "2"
}

リクエストの出力(Controller内で標準出力)
enumは正常に変換される。

JsonObject(stringValue=test, enumValue=ENUM_TYPE_3)

不正な列挙順で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "3"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `sample.JsonEnum` from String "3": not one of the values accepted for Enum class: [ENUM_TYPE_1, ENUM_TYPE_2, ENUM_TYPE_3]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `sample.JsonEnum` from String "3": not one of the values accepted for Enum class: [ENUM_TYPE_1, ENUM_TYPE_2, ENUM_TYPE_3]<EOL> at [Source: (PushbackInputStream); line: 1, column: 36] (through reference chain: sample.JsonObject["enumValue"])]

enumのプロパティ名を文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "type_2"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `sample.JsonEnum` from String "type_3": not one of the values accepted for Enum class: [ENUM_TYPE_1, ENUM_TYPE_2, ENUM_TYPE_3]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `sample.JsonEnum` from String "type_3": not one of the values accepted for Enum class: [ENUM_TYPE_1, ENUM_TYPE_2, ENUM_TYPE_3]<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

アノテーションあり

enumにJsonから変換する際に使用するプロパティに@JsonValueのアノテーション付与

enum.kt
public enum class JsonEnum(
    @JsonValue
    val value: String
) {
    ENUM_TYPE_1("type_1"),
    ENUM_TYPE_2("type_2"),
    ENUM_TYPE_3("type_3");
}

enumを表す任意の文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "type_2"
}

リクエストの出力(Controller内で標準出力)
enumは正常に変換される。

JsonObject(stringValue=test, enumValue=ENUM_TYPE_2)

不正なenumを表す文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "type_4"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `sample.JsonEnum` from String "type_4": not one of the values accepted for Enum class: [type_1, type_2, type_3]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `sample.JsonEnum` from String "type_4": not one of the values accepted for Enum class: [type_1, type_2, type_3]<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

enumの0から始まる列挙順で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "1"
}

リクエストの出力(Controller内で標準出力)
enumは正常に変換される。

JsonObject(stringValue=test, enumValue=ENUM_TYPE_2)

不正な列挙順で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "3"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `sample.JsonEnum` from String "3": not one of the values accepted for Enum class: [type_1, type_2, type_3]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `sample.JsonEnum` from String "3": not one of the values accepted for Enum class: [type_1, type_2, type_3]<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

enum名を表す文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "ENUM_TYPE_1"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot deserialize value of type `sample.JsonEnum` from String "ENUM_TYPE_1": not one of the values accepted for Enum class: [type_1, type_2, type_3]; nested exception is com.fasterxml.jackson.databind.exc.InvalidFormatException: Cannot deserialize value of type `sample.JsonEnum` from String "ENUM_TYPE_1": not one of the values accepted for Enum class: [type_1, type_2, type_3]<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

列挙順の数値と@JsonValueに指定した値から変換できてしまうので、少しややこしいです。
@JsonValueに指定した値が数値だとよりわかりにくくなりそうな気が。。
後述の@JsonCreatorを追加したほうが無難です。

アノテーションあり(enum生成処理あり)

enum生成処理に@JsonCreatorを付与

enum.kt
public enum class JsonEnum(
    @JsonValue
    val value: String
) {
    ENUM_TYPE_1("type_1"),
    ENUM_TYPE_2("type_2"),
    ENUM_TYPE_3("type_3");

    companion object {
        @JvmStatic
        @JsonCreator(mode = JsonCreator.Mode.DELEGATING)
        fun fromJson(value: String): JsonEnum {
            return values().find { it.value == value }
                ?: throw IllegalArgumentException("Bad Request $value")
        }
    }
}

Kotlinの場合、@JsonCreatorを使用するcompanion object(≒static)関数には、@JvmStaticをつけてあげないとライブラリから関数を見つけることができない。
@JsonCreator関してはmodeを指定しないとエラーが発生する。(後述)

enumを表す任意の文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "type_1"
}

リクエストの出力(Controller内で標準出力)
enumは正常に変換される。

JsonObject(stringValue=test, enumValue=ENUM_TYPE_1)

不正なenumを表す文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "type_0"
}

例外発生
作成したenum生成処理(companion object)内での発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `sample.JsonEnum`, problem: Bad Request type_0; nested exception is com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `sample.JsonEnum`, problem: Bad Request type_0<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

enumの0から始まる列挙順で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "2"
}

例外発生
作成したenum生成処理(companion object)内での発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `sample.JsonEnum`, problem: Bad Request 2; nested exception is com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `sample.JsonEnum`, problem: Bad Request 2<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

enum名を表す文字列で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "ENUM_TYPE_1"
}

例外発生
作成したenum生成処理(companion object)内での発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Cannot construct instance of `sample.JsonEnum`, problem: Bad Request ENUM_TYPE_1; nested exception is com.fasterxml.jackson.databind.exc.ValueInstantiationException: Cannot construct instance of `sample.JsonEnum`, problem: Bad Request ENUM_TYPE_1<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

@JsonCreatorのmode指定に関して

以下で報告されているようにmode指定しないとエラーが発生する。
https://github.com/FasterXML/jackson-module-kotlin/issues/336
mode指定なしのenum

enum.kt
public enum class JsonEnum(
    @JsonValue
    val value: String
) {
    ENUM_TYPE_1("type_1"),
    ENUM_TYPE_2("type_2"),
    ENUM_TYPE_3("type_3");

    companion object {
        @JvmStatic
        @JsonCreator
        fun fromJson(value: String): JsonEnum {
            return values().find { it.value == value }
                ?: throw IllegalArgumentException("Bad Request $value")
        }
    }
}

正常に動いたものと同じ値で送信

Request.json
{
  "stringValue": "test",
  "enumValue": "type_1"
}

例外発生

Resolved [org.springframework.http.converter.HttpMessageNotReadableException: JSON parse error: Input mismatch reading Enum `sample.JsonEnum`: properties-based `@JsonCreator` ([method sample.JsonEnum#fromJson(java.lang.String)]) expects JSON Object (JsonToken.START_OBJECT), got JsonToken.VALUE_STRING; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Input mismatch reading Enum `sample.JsonEnum`: properties-based `@JsonCreator` ([method sample.JsonEnum#fromJson(java.lang.String)]) expects JSON Object (JsonToken.START_OBJECT), got JsonToken.VALUE_STRING<EOL> at [Source: (PushbackInputStream); line: 1, column: 35] (through reference chain: sample.JsonObject["enumValue"])]

まとめ

アノテーション enum名 列挙順 指定の値
なし
@JsonValue
@JsonValue+@JsonCreator
1
0
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
1
0