μ½νλ¦°κ³Ό Jackson λΌμ΄λΈλ¬λ¦¬
κ°μΈμ μΌλ‘ μλμ§ μ¬μ μμ μ¬μ©λλ KPXμ μ€κ°κ±°λ APIμ λν λͺ¨νΉ μνμ λ§λ€μ΄λ³΄λ©΄μ μ½νλ¦° κΈ°λ° μ€νλ§ μ ν리μΌμ΄μ μ νμ΅ν΄λ³΄κ³ μμ΅λλ€. μ΄ κ³Όμ μμ κ°λ¨νκ² κ²½νν Jackson κ΄λ ¨ λ΄μ©μ μ μ΄λ³΄κ³ μ ν©λλ€. λ³Έ κΈμ μμ±λ κ²μ μ€μ§ μ½νλ¦°μλ§ ν΄λΉνλ λ΄μ©μ΄ μλ μ μμΌλ―λ‘ μ£Όμνμ¬ μ°Έκ³ νμκΈΈ λ°λλλ€.
build.gradle.ktsdependencies { implementation("com.fasterxml.jackson.module:jackson-module-kotlin") }
Jackson λΌμ΄λΈλ¬λ¦¬μμλ μ½νλ¦° μ§μμ μν΄ jackson-module-kotlin λͺ¨λμ μ 곡νκ³ μμ΅λλ€. κ·Έλμ μ½νλ¦° κΈ°λ° μ€νλ§ νλ μμν¬μμλ JSON μ§λ ¬νλ₯Ό μν΄μ Jackson λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©νκ² λλλ΄ λλ€. μ€νλ§ λΆνΈ μλ μ€μ μμλ μ½νλ¦° μ§μμΌλ‘ μ½νλ¦° λͺ¨λμ΄ ν΄λμ€ν¨μ€μ μμΌλ©΄ μλμΌλ‘ λ±λ‘νκ³ ν΄μ£Όκ³ μμμ΄ μλμ κ°μ΄ λμ μμ΅λλ€.
Jacksonβs Kotlin module is required for serializing / deserializing JSON data in Kotlin. It is automatically registered when found on the classpath. A warning message is logged if Jackson and Kotlin are present but the Jackson Kotlin module is not.
μ μ€νλ§ κ³΅μ λ¬Έμμ λ΄μ©μ λ°λ₯΄λ©΄ ν΄λμ€ ν¨μ€μ μ½νλ¦° λͺ¨λμ΄ ν¬ν¨λμ΄ μμ§ μλ€λ©΄ WARN λ‘κ·Έλ₯Ό μΆλ ₯ν΄μ€λ€κ³ λμ΄μμ§λ§ μ½νλ¦° λͺ¨λμ λν μμ‘΄μ±μ μ κ±°νκ³ μ ν리μΌμ΄μ μ μ€νν΄λ κ΄λ ¨ λ‘κ·Έλ νμΈν μ μμμ΅λλ€. κ·Έλ¦¬κ³ λ§μ½, μλ μ€μ μ΄ μλ μ ν리μΌμ΄μ μ μν΄ μμΈν μ€μ μ μν΄ λ³λμ λΉμΌλ‘ λ±λ‘νλ€λ©΄ μ½νλ¦° κΈ°λ° μ€νλ§ μ ν리μΌμ΄μ μμ ObjectMapperμ μ½νλ¦° λͺ¨λμ΄ μ λλ‘ λ±λ‘λμ΄μλμ§ κ²μ¦ν΄μΌν κ² κ°μ΅λλ€.
μ λ€λ¦ μ νμ μν TypeReference
μλ° κΈ°λ° μ½λμμλ μ λ€λ¦ μ νμ λν μ§λ ¬νλ₯Ό μν΄μ TypeReference<>() {} μ κ°μ ννλ‘ μ½λλ₯Ό μμ±ν΄μΌλ§ νμ΅λλ€. μ½νλ¦° λͺ¨λμλ readValueμ convertValue μ λν νμ₯ ν¨μλ₯Ό ν¬ν¨νκ³ μμ΄ κ΅¬μ²΄μ μΈ ν΄λμ€ μ 보λ₯Ό μΆλ‘ ν μ μμ΅λλ€.
import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.fasterxml.jackson.module.kotlin.convertValue
data class MyStateObject(val name: String, val age: Int)
val obj = mapper.readValue<MyStateObject>(json)
Flatten νκ² μ§λ ¬νν μ μλ @JsonUnwrapped
{
"FORE_SET_GEN_ID": "V000",
"TRADE_DAY": "20210601",
"FORECAST_VALUES": [
{
"CBP_GEN_ID": "AA11",
"ESS_MTR_TYPE": 0,
"HH_01": 0,
"HH_02": 0,
"HH_03": 0,
"HH_04": 0,
"HH_05": 0,
"HH_06": 0,
"HH_07": 0,
"HH_08": 100.223,
"HH_09": 100.223,
"HH_10": 100.223,
"HH_11": 100.223,
"HH_12": 100.223,
"HH_13": 100.223,
"HH_14": 100.223,
"HH_15": 100.223,
"HH_16": 100.223,
"HH_17": 100.223,
"HH_18": 100.223,
"HH_19": 0,
"HH_20": 0,
"HH_21": 0,
"HH_22": 0,
"HH_23": 0,
"HH_24": 0
}
]
}
Jackson μμ μ 곡ν΄μ£Όλ @JsonUnwrapped μ΄λ Έν μ΄μ μ λΉ ν΄λμ€μ ν¬ν¨λ νλμ λν΄μ Flattened λμ΄ νμ΄ν€μ³μ§ ννμ JSONμΌλ‘ μ§λ ¬νν μ μλ λ°©λ²μ μ 곡ν©λλ€. KPX μ€κ°κ±°λ API μ€ μμΈ‘κ° μ μΆμμλ HH_01 λΆν° HH_24 κΉμ§ HH_XX ννλ‘ μ λ¬λλ μκ° νλ μ λ³΄κ° ν¬ν¨λκΈ° λλ¬Έμ, λͺ¨λ μκ°μ λμ΄νκΈ° 보λ€λ κ° μκ°μ΄ Keyλ‘ κ΅¬μ±λλ MapμΌλ‘ λ νλμ @JsonUnwrapped λ₯Ό μ¬μ©νλκ² μ’μ κ² κ°λ€λ μκ°μ νμ΅λλ€.
combination not yet supported
com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot define Creator property "values" as @JsonUnwrapped: combination not yet supported
μλ° κΈ°λ° μ½λμ λ€λ₯΄κ² μ½νλ¦°μμλ μ΄λ Έν μ΄μ μ κ·Έλλ‘ μ¬μ©νλ©΄ μμ κ°μ μ€λ₯κ° λ°μν μ μμ΅λλ€. μ½νλ¦° λͺ¨λ μ΄μμ μ€λλ μ‘°μΈλλ‘ μλνλ λΆλΆμ Prefixλ‘ μ 곡ν΄μΌνλ κ² κ°μ΅λλ€. κ·Έλ¬λ, @field:JsonUnwrapperd λ‘ λ°κΎΈμ΄λ JSON λ°μ΄ν°λ‘ μ λ¬λλ μμ²μ λ³ννλ κ³Όμ μμλ λμΌν μ€λ₯λ₯Ό νμΈν μ μμ΅λλ€. Jackson μ μ§λ ¬νλ₯Ό μννλ κ³Όμ μμ μμ±μ κ·Έλ¦¬κ³ Getterμ Setterλ₯Ό νμ©νκ² λ©λλ€. κ·Έλ°λ° Map ν΄λμ€ νΉμ± μ Getterμ Setter ν¨μκ° μ‘΄μ¬νμ§ μλ νΉμ΄ν ν΄λμ€κ° λ©λλ€. κ·Έλμ @JsonAnyGetter μ @JsonAnySetterμ κ°μ μ΄λ Έν μ΄μ μ λμμ΄ νμνκ² λ©λλ€.
The following declarations have the same JVM signature
Kotlin: Platform declaration clash: The following declarations have the same JVM signature (getValues()Ljava/util/Map;):
fun `<get-values>`(): MutableMap<String, Double> defined in kr.kdev.mock.kpx.model.ForecastValue
fun getValues(): Map<String, Double> defined in kr.kdev.mock.kpx.model.ForecastValue
무μμ @JsonAnyGetterλ₯Ό μΆκ°ν΄λ²λ¦¬λ©΄ μ½νλ¦°μμ data ν΄λμ€μ νλλ‘ μΈν΄ λ§λ€μ΄μ§λ Getter ν¨μμ μκ·Έλμ² μ€λ³΅ λ¬Έμ κ° λ°μν μ μμ΅λλ€. μ΄ κ²½μ° λ€μκ³Ό κ°μ΄ μ€λ³΅λλ νλμ private ν€μλλ₯Ό μ μΈνλ©΄ λ¬Έμ μμ λ²μ΄λ μ μμ΅λλ€. λν, μ°μ°μ μ€λ²λ‘λ©μΌλ‘ κ°μ κ°μ Έμ€κ³ μ€μ νλκ² λ κ°νΈν΄μ‘μ΅λλ€.
ForecastValue.kt@JsonNaming(PropertyNamingStrategies.UpperSnakeCaseStrategy::class) data class ForecastValue( val cbpGenId: String, val essMtrType: Int = 0, @JsonIgnore // NOTE: @JsonUnwrapped not support map object. private var values: MutableMap<String, Double> = mutableMapOf(), ) { @JsonAnyGetter fun getValues(): Map<String, Double> = values @JsonAnySetter fun setValue( key: String, value: Double, ) { values[key] = value } operator fun get(index: Int): Double = values["HH_%02d".format(index)] ?: 0.0 // EXAMPLE: (1..24).forEach { forecastValue[it] = 0.0 } operator fun set( index: Int, value: Double, ) { values["HH_%02d".format(index)] = value } }
UPPER_SNAKE_CASE μ @ModelAttribute
KPX μ€κ°κ±°λ APIλ λλ¬Έμμ μΈλμ€μ½μ΄(_)λ‘ μ΄λ£¨μ΄μ§λ UPPER_SNAKE_CASEλ‘ νλλͺ μ΄ κ΅¬μ±λλ κ²μ νμΈν μ μμ΅λλ€. @JsonNamingμ JSON μ§λ ¬ν μ λ³ννλ λ°©μμ λ³λλ‘ μ§μ ν μ μκ² μ§μνλ―λ‘ μμ ForecastValue λ°μ΄ν° ν΄λμ€μ PropertyNamingStrategies.UpperSnakeCaseStrategy κ° μ μ©λ κ²μ νμΈν μ μμ΅λλ€.
Terminal{"HTTP_CODE":201,"HTTP_STATUS":"CREATED","DATA":{"FORE_SET_GEN_ID":"V001","TRADE_DAY":"20250219","FORECAST_VALUES":[{"CBP_GEN_ID":"A011","ESS_MTR_TYPE":0,"HH_01":0.0,"HH_02":0.0,"HH_03":0.0,"HH_04":0.0,"HH_05":0.0,"HH_06":0.0,"HH_07":0.0,"HH_08":0.0,"HH_09":0.0,"HH_10":0.0,"HH_11":0.0,"HH_12":0.0,"HH_13":0.0,"HH_14":0.0,"HH_15":0.0,"HH_16":0.0,"HH_17":0.0,"HH_18":0.0,"HH_19":0.0,"HH_20":0.0,"HH_21":0.0,"HH_22":0.0,"HH_23":0.0,"HH_24":0.0}]}}
@ModelAttributeλ μ€νλ§ μ»¨νΈλ‘€λ¬μ νΈλ€λ¬ ν¨μ νλΌλ―Έν° μ μ μ λ°μ΄ν° λ°μΈλ©μ μν΄μ μ¬μ©νλ μ΄λ Έν μ΄μ μΌλ‘ μμ£Ό μ¬μ©νλ κ²μ λλ€. κ·Έλ°λ°, @ModelAttributeλ JSON μ§λ ¬ν λ°©μμ΄ μλλ―λ‘ μ§μνμ§ μμΌλ―λ‘ @ModelAttributeκ° μλ @RequestParamμ μ¬μ©νμ¬ UPPER_SNAKE_CASE ννλ‘ μ§μ ν΄μΌν©λλ€. λ€μμ μμΈ‘κ° μ μΆ μ‘°ν APIμ μμ² μΏΌλ¦¬ νλΌλ―Έν° λΆλΆμ μ μν κ²μ λν μμμ λλ€.
@GetMapping("/forecast/metering")
fun getForecastMetering(
@RequestParam("FORE_SET_GEN_ID") foreSetGenId: String,
@DateTimeFormat(pattern = "yyyyMMdd") @RequestParam("TRADE_DAY") date: LocalDate,
): ForecastMeteringResponse {
}
KPX μ€κ°κ±°λ API κ°μ΄λ λ¬Έμλ₯Ό 보면 v1 μμ v2λ‘ λμ΄κ°λ©΄μ UPPER_SNAKE_CASEλ‘ μΌκ΄ λ³κ²½λ κ±Έ μΈμ§ν μ μμ΅λλ€. μ΄λ€ μΈμ΄λ‘ μμ±λ μλ²μΈμ§λ λͺ¨λ₯΄κ² μΌλ λλΆλΆμ κ²½μ° μΉ΄λ© μΌμ΄μ€λ₯Ό μ±ννλ κ²½μ°κ° λ§μμ μλ° κ°λ°μ μ μ₯μμλ μκ°λ³΄λ€ λΆνΈν λΆλΆμ΄λΌκ³ λκ»΄μ§κ² λ©λλ€.
μ΄μ λ.