π Spring - @JsonProperty, @JsonNaming
μλ
νμΈμ, μ΄λ²μ μ 리ν λ΄μ©μ μ€νλ§μμ @JsonProperty, @JsonNaming μ΄λ
Έν
μ΄μ
μ
λλ€.
REST API λ°©μμΌλ‘ μλ²μ ν΄λΌμ΄μΈνΈκ° λ°μ΄ν°λ₯Ό ν΅μ μ ν λ JSON νμμ μ£Όλ‘ μ¬μ©ν©λλ€.
μλ²λ¨μμλ μΉ΄λ© μΌμ΄μ€(Camel Case) λ°©μμ, ν΄λΌμ΄μΈνΈλ¨μμλ μ€λ€μ΄ν¬ μΌμ΄μ€(Snake Case) λ°©μμ μ¬μ©ν©λλ€.
μΉ΄λ© μΌμ΄μ€(Camel Case)
- 첫 κΈμλ μλ¬Έμλ‘, μ€κ° κΈμλ€μ λλ¬Έμλ‘ μμνλ νκΈ°λ²
- ex) phoneNumber, postMapping
μ€λ€μ΄ν¬ μΌμ΄μ€(Snake Case)
- μΈλλ°(_)κ° ν¬ν¨λ νν λ°©μ
- ex) phone_number, post_mapping
λ³΄ν΅ μλ°λ μΉ΄λ© μΌμ΄μ€λ₯Ό, JSONνμμ μ€ν μ΄ν¬ μΌμ΄μ€ λ°©μμΌλ‘ ννΈμ νλκ² μμΉμ λλ€.
μ λ€λͺ¨λ°μ€μ²λΌ ν΄λΌμ΄μΈνΈμ μλ²λ¨μ νν λ°©μμ΄ λ€λ¦μ λ°λΌ λ°μ΄ν°μ Keyκ° λ¬λΌμ§λ κ²½μ°κ° μ‘΄μ¬ν©λλ€.
μ΄λ¬ν λ¬Έμ λ₯Ό ν΄κ²°ν λ @JsonProperty, @JsonNaming μ΄λ Έν μ΄μ μ μ¬μ©ν μ μμ΅λλ€.
π― Kotlin REST API μ½λ
μμ λ Kotlin κΈ°λ°μΌλ‘ μμ±μ νμμ΅λλ€.
μμ μ½λλ κΉνλΈ μμ νμΈν μ μμ΅λλ€ :)
UserRequest
package org.juhyun.kotlinspringboot.model
data class UserRequest (
var name:String?=null,
var age:Int?=null,
var email:String?=null,
var address:String?=null,
var phoneNumber:String?=null
)
UserRequest κ°μ²΄λ ν΄λΌμ΄μΈνΈμ μλ²μμ ν΅μ μ ν λ μ¬μ©λλ κ°μ²΄μ λλ€.
ν΄λΉ λ³μλ μΉ΄λ© μΌμ΄μ€ νκΈ°λ²μ λ°λ₯΄κ³ μμ΅λλ€.
Controller
package org.juhyun.kotlinspringboot.controller
import org.juhyun.kotlinspringboot.model.UserRequest
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/api")
class PostApiController {
@PostMapping("/post-mapping")
fun postMapping(): String {
return "post-mapping"
}
@PostMapping("/post-mapping/object")
fun postMappingObject(@RequestBody userRequest: UserRequest): UserRequest {
println(userRequest) // json -> object
return userRequest // object -> json
}
}
컨νΈλ‘€λ¬μλ postMappingObject() λ©μλμμ UserRequest κ°μ²΄λ₯Ό λ°μμ μ¬μ©νκ³ μμ΅λλ€.
Talend API Testerλ₯Ό ν΅ν΄ μ APIλ₯Ό ν μ€νΈν΄λ³΄κ² μ΅λλ€.
Talend API Tester
μ APIμ κ²°κ³Όμμ νμΈνλ― phoneNumber λ³μμλ λ°μ΄ν°κ° μ μ₯μ΄ λμ§ μμλλ°μ, μ΄λ APIμ keyμ μλ΅ κ°μ²΄μ νλλͺ μ΄ λ€λ₯΄κΈ° λλ¬Έμ λ°μνλ λ¬Έμ μ λλ€.
μ€μ μλ²μμμ κ²°κ³Όμλ nullλ‘ μΆλ ₯μ΄ λ©λλ€.
μ΄μ @JsonProperty μ΄λ Έν μ΄μ κ³Ό @JsonNaming μ΄λ Έν μ΄μ μ ν΅ν΄ λ¬Έμ λ₯Ό ν΄κ²°ν΄λ³΄κ² μ΅λλ€.
@JsonProperty
package com.fasterxml.jackson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marker annotation that can be used to define a non-static
* method as a "setter" or "getter" for a logical property
* (depending on its signature),
* or non-static object field to be used (serialized, deserialized) as
* a logical property.
*<p>
* Default value ("") indicates that the field name is used
* as the property name without any modifications, but it
* can be specified to non-empty value to specify different
* name. Property name refers to name used externally, as
* the field name in JSON objects.
*<p>
* Starting with Jackson 2.6 this annotation may also be
* used to change serialization of <code>Enum</code> like so:
*<pre>
public enum MyEnum {
{@literal @JsonProperty}("theFirstValue") THE_FIRST_VALUE,
{@literal @JsonProperty}("another_value") ANOTHER_VALUE;
}
</pre>
* as an alternative to using {@link JsonValue} annotation.
*<p>
* Starting with Jackson 2.12 it is also possible to specify {@code namespace}
* of property: this property is only used by certain format backends (most
* notably XML).
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface JsonProperty
{
/**
* Special value that indicates that handlers should use the default
* name (derived from method or field name) for property.
*
* @since 2.1
*/
public final static String USE_DEFAULT_NAME = "";
...
}
@JsonProperty μ΄λ Έν μ΄μ μ κ°μ²΄λ₯Ό JSON νμμΌλ‘ λ³νν λ Keyμ μ΄λ¦μ μ€μ ν μ μμ΅λλ€.
μμμ ν΄λΌμ΄μΈνΈμ μλ²μ νκΈ°λ²μ΄ λ¬λΌμ λ°μνλ λ¬Έμ λ₯Ό @JsonProperty μ΄λ Έν μ΄μ μ ν΅ν΄ ν΄κ²°ν΄λ³΄κ² μ΅λλ€.
package org.juhyun.kotlinspringboot.model
import com.fasterxml.jackson.annotation.JsonProperty
data class UserRequest (
var name:String?=null,
var age:Int?=null,
var email:String?=null,
var address:String?=null,
@JsonProperty("phone_number")
var phoneNumber:String?=null // jsonμμλ phone_number
)
phoneNumber νλλ μΉ΄λ© μΌμ΄μ€λ‘ μμ±μ νκ³ , @JsonProperty μ΄λ Έν μ΄μ μ ν΅ν΄ JSON λ³ν μ λ°μ Keyμ κ°μ μ€μ ν©λλ€.
μ΄λ Έν μ΄μ μ μ μ©ν ν API ν μ€νΈλ₯Ό μ§νν΄λ³΄λ©΄ λ°μ΄ν°κ° μ μμ μΌλ‘ λμ΄μ€λ κ±Έ νμΈν μ μμ΅λλ€.
@JsonNaming
package com.fasterxml.jackson.databind.annotation;
import java.lang.annotation.*;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
/**
* Annotation that can be used to indicate a {@link PropertyNamingStrategy}
* to use for annotated class. Overrides the global (default) strategy.
* Note that if the {@link #value} property is omitted, its default value
* means "use default naming" (that is, no alternate naming method is used).
* This can be used as an override with mix-ins.
*
* @since 2.1
*/
@Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@com.fasterxml.jackson.annotation.JacksonAnnotation
public @interface JsonNaming
{
/**
* @return Type of {@link PropertyNamingStrategy} to use, if any; default value of
* <code>PropertyNamingStrategy.class</code> means "no strategy specified"
* (and may also be used for overriding to remove otherwise applicable
* naming strategy)
*/
public Class<? extends PropertyNamingStrategy> value() default PropertyNamingStrategy.class;
}
νμ¬ μν©μ UserRequest κ°μ²΄μμ νλμ νλλ§ μ€μ μ νμ§λ§, μ΄λ¬ν μ€μ μ λ€μν λ³μμμ μ€μ νλ €λ©΄ λͺ¨λ νλμ
@JsonProperty μ΄λ Έν μ΄μ μ μ μ©ν΄μ£Όμ΄μΌ νκΈ° λλ¬Έμ λ²κ±°λ‘μ΄ μμ μ΄ λ μ μμ΅λλ€.
μ΄λ¬ν λ¬Έμ λ @JsonNaming μ΄λ Έν μ΄μ μ ν΅ν΄ ν΄λμ€μ μ μ©ν¨μΌλ‘μ¨ ν΄κ²°ν μ μμ΅λλ€.
package org.juhyun.kotlinspringboot.model
import com.fasterxml.jackson.databind.PropertyNamingStrategies
import com.fasterxml.jackson.databind.PropertyNamingStrategy
import com.fasterxml.jackson.databind.annotation.JsonNaming
// JSON λ€μ΄λ° μ λ΅
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy::class)
// @JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy::class) deprecated
data class UserRequest (
var name:String?=null,
var age:Int?=null,
var email:String?=null,
var address:String?=null,
var phoneNumber:String?=null
)
β» κΈ°μ‘΄μ μ¬μ©νλ PropertyNamingStrategy ν΄λμ€λ Deprecatedκ° λμμ΅λλ€.
λ°λΌμ Kotlinμμ Jackson Moduleμ 2.12 λ²μ λΆν°λ PropertyNamingStrategies ν΄λμ€μ μ¬μ©μ κΆμ₯νκ³ μμ΅λλ€.
ν΄λΉ λ¬Έμ μ λν΄ κΆκΈνμλ©΄ λ§ν¬ λ₯Ό μ°Έκ³ ν΄μ£ΌμΈμ!
μμ κ°μ΄ @JsonNaming μ΄λ Έν μ΄μ μ μ μ©νκ³ API ν μ€νΈλ₯Ό ν΄λ³΄λ©΄ phoneNumber νλμ μ μμ μΌλ‘ λ°μ΄ν°κ° λ€μ΄μ¨ κ±Έ νμΈν μ μμ΅λλ€.
{
"name": "JuHyun",
"age": 20,
"email": "a@a.com",
"address": "Seoul",
"phone_number": "010-1234-1234"
}
References
'Spring' μΉ΄ν κ³ λ¦¬μ λ€λ₯Έ κΈ
Spring Validation - @NotNull, @NotEmpty, @NotBlank (0) | 2021.09.09 |
---|---|
Spring Boot + MockMvc ν μ€νΈ(feat. Kotlin) (0) | 2021.09.07 |
Spring AOP - (1) νλ‘μ ν¨ν΄, λ°μ½λ μ΄ν° ν¨ν΄ (0) | 2021.08.27 |
Spring Boot Maven profile μ΄μ & κ°λ° DB λΆλ¦¬(AWS EC2) (6) | 2021.06.18 |
JUnit - @ParameterizedTest, @ValueSource, @CsvSource, @MethodSource μ΄λ Έν μ΄μ (4) | 2021.06.08 |
λκΈ