11
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

[Spring Boot] リクエストのJSONをオブジェクトにマッピングするときの注意点 [Kotlin]

Last updated at Posted at 2018-09-10

本記事ではSpring Bootで@RequestBodyアノテーションを使用し、
JSONがオブジェクトにマッピングされる際の注意点をお伝えします。
※ Kotlinでの挙動となります。

結論から述べると、non-nullable型にマッピングする際はデフォルト値に気をつけたほうがいいです!

Nullableなプロパティにマッピングする場合

以下がサンプルのコードです。

NullableController.kt
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
class NullableController {
    @PostMapping("/nullable")
    fun nullable(@RequestBody nullableType: NullableType) = nullableType
}

data class NullableType(
        val int: Int?,
        val double: Double?,
        val float: Float?,
        val long: Long?,
        val short: Short?,
        val byte: Byte?,
        val char: Char?,
        val boolean: Boolean?,
        val string: String?
)

上記コードでは@RequestBodyアノテーションをもちいて、リクエストのJSONをオブジェクトへマッピングしています。
マッピングされるNullableTypeはデータクラスで、すべてのプロパティがnullableで定義されています。
こちらのAPIにリクエストを投げると以下のように返ってきます。

$ curl -X POST localhost:8080/nullable -H "Content-Type:application/json" -d '{"int": 1, "double": 1.1, "float": 1.2, "long": 2, "short": 3, "byte": 4, "char": "5", "boolean": true, "string": "test"}'
{"int":1,"double":1.1,"float":1.2,"long":2,"short":3,"byte":4,"char":"5","boolean":true,"string":"test"}

では、今度はカラのJSONを投げたとき、NullableTypeはどのようにマッピングされるでしょうか?
正解はこちら↓

$ curl -X POST localhost:8080/nullable -H "Content-Type:application/json" -d '{}'
{"int":null,"double":null,"float":null,"long":null,"short":null,"byte":null,"char":null,"boolean":null,"string":null}

NullableTypeプロパティはすべてnullableであるため、リクエストのJSONに定義されていないパラメータはnullとして定義されました。

Non-nullなプロパティにマッピングする場合

以下がサンプルのコードです。

NonNullController.kt
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RestController

@RestController
class NonNullController {
    @PostMapping("/non-null")
    fun nonNull(@RequestBody nonNullType: NonNullType) = nonNullType
}

data class NonNullType(
        val int: Int,
        val double: Double,
        val float: Float,
        val long: Long,
        val short: Short,
        val byte: Byte,
        val char: Char,
        val boolean: Boolean,
        val string: String
)

こちらはマッピングされるNonNullTypeのプロパティがすべてnon-nullで定義されています。
こちらのAPIもリクエストを投げると以下のように返ってきます。

$ curl -X POST localhost:8080/non-null -H "Content-Type:application/json" -d '{"int": 1, "double": 1.1, "float": 1.2, "long": 2, "short": 3, "byte": 4, "char": "5", "boolean": true, "string": "test"}'
{"int":1,"double":1.1,"float":1.2,"long":2,"short":3,"byte":4,"char":"5","boolean":true,"string":"test"}

つぎにNullableのときと同様にカラのJSONを投げるのですが、プロパティの型によってマッピングの挙動が異なります。

JSON内でString型のプロパティが定義されていない場合

前述のリクエストからString型のパラメータだけ削除してPOSTしてみるとどうなるでしょうか?

$ curl -X POST localhost:8080/non-null -H "Content-Type:application/json" -d '{"int": 1, "double": 1.1, "float": 1.2, "long": 2, "short": 3, "byte": 4, "char": "5", "boolean": true}'
{"timestamp":"2018-09-10T00:00:00.000+0000","status":400,"error":"Bad Request","message":"JSON parse error: Instantiation of [simple type, class com.example.demo.NonNullType] value failed for JSON property string due to missing (therefore NULL) value for creator parameter string which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.example.demo.NonNullType] value failed for JSON property string due to missing (therefore NULL) value for creator parameter string which is a non-nullable type\n at [Source: (PushbackInputStream); line: 1, column: 103] (through reference chain: com.example.demo.NonNullType[\"string\"])","path":"/non-null"}

結果としてBad Requestが返ってきました。
NonNullTypeでstringプロパティが定義されているため、JSON parse errorとして怒られるのは当然のように思えます。

JSON内でプリミティブ型のプロパティが定義されていない場合

こんどは前述のリクエストのString型のパラメータ以外を削除してPOSTしてみます。

$ curl -X POST localhost:8080/non-null -H "Content-Type:application/json" -d '{"string": "test"}
{"int":0,"double":0.0,"float":0.0,"long":0,"short":0,"byte":0,"char":"\u0000","boolean":false,"string":"test"}

結果はJSONにパラメータを渡していなかったにもかかわらず、デフォルト値を付与したうえでNonNullTypeにマッピングされました。
jackson-kotlin-moduleにおいて、Javaのプリミティブ型にあたるプロパティはデフォルト値が付与された状態でマッピングされるようです。
もし参照型と同様に、JSON内でパラメータが存在しないときBad Requestを返すようにするには、以下の設定が必要です。

application.properties
spring.jackson.deserialization.fail-on-null-for-primitives=true

参考

Using Kotlin Default Parameter Values when JSON value is null and Kotlin parameter type is Non-Nullable #130

11
9
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
11
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?