1. 객체의 커스텀 직렬화
직렬화 관련 TIL) 을 세번쨰 연속 쓰고 있다.. 뭔가 겹치는 부분도 있고 해서 좀 찝찝하지만.. 새로운 부분이 있어서 그냥 적는다..
이번에 해본 내용은 객체의 커스텀한 직렬화이다.
일단 클래스를 하나 만든다.
class Family(
val name: String,
val number: Int,
val people: List<FamilyPeople>
)
class FamilyPeople(
val name: String,
val age: Int
)
1-1 객체의 커스텀 Serializer/Deserializer
[직렬화]
class FamilySerializer: JsonSerializer<Family>() {
override fun serialize(value: Family, generator: JsonGenerator, provider: SerializerProvider) {
generator.writeStartObject();
generator.writeFieldName("[name]")
generator.writeString(value.name + "Family")
generator.writeFieldName("[number]")
generator.writeString(value.number.toString() + "people")
generator.writeFieldName("people")
generator.writeObject(value.people)
generator.writeEndObject()
}
}
인자로 들어오는 JsonGenerator 타입의 객체에는 Json 을 만들 수 있는 기능이 있다.
generator.writeStartObject() : json 의 { 와 같다.
generator.writeFieldName() : json 의 "name" 과 같은 키값을 적는다.
generator.writeString() : json 에 String 값을 적는다. 벨류값을 적을 때 사용한다.
generator.writeEndObject() : json 의 } 와 같다.
[역직렬화]
class FamilyDeserializer : JsonDeserializer<Family>() {
private val objectMapper: ObjectMapper = ObjectMapper()
override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Family {
val objectCodec = p.codec
val jsonNode: JsonNode = objectCodec.readTree(p)
val name = jsonNode["name"].asText() + " Family"
val number = jsonNode["number"].asInt() + 1000
val people = objectMapper.convertValue(jsonNode["people"], List::class.java)
return Family(name, number, people as List<FamilyPeople>)
}
init {
val simpleModule = SimpleModule().apply {
// addDeserializer()
}
objectMapper.registerModule(simpleModule)
}
}
역직렬화에는, json 을 객체로 만들어야 하기에 ObjectMapper 가 필요하다.
클래스의 내부에 다른 중첩 객체가 있고, 그 객체의 직렬화 로직이 커스텀하다면, ObjectMapper 를 생성할때,
Kotlin 의 init() 안에서 등록한다.
이후 JsonParser 의 codec 값을 읽어서 객체를 만든다.
1-2. 만든 Serializer/Deserializer 를 적용하는 방법에는 3개가 있다.
- @JsonSerializer / @JsonDeserializer.
- 코드 상에서 직접 ObjectMapper 를 등록해서 사용하는 방법.
- WebConfigurer 의 extendMessageConverter() 에서 ObjectMapper 를 등록한 MappingJackson2HttpMessageConverter 를 등록해서 사용.
3번째 방법은 이전 글에서 다루었기 때문에, 넘어가고
1번째 방법은 객체에 에너테이션만 붙이면 된다.
이번에는 두번째 코드 상에서 직접 ObjectMapper 를 등록해서 사용하는 방법을 적어본다.
class SerializeTest {
private lateinit var objectMapper: ObjectMapper
@BeforeEach
fun register() {
val simpleModule = SimpleModule().apply {
addSerializer(Family::class.java, FamilySerializer())
addDeserializer(Family::class.java, FamilyDeserializer())
}
objectMapper = ObjectMapper().apply {
registerModule(simpleModule)
}
}
@Test
fun serialize() {
val p1 = FamilyPeople("tomas", 27)
val p2 = FamilyPeople("eil", 47)
val p3 = FamilyPeople("json", 22)
val myFamily = Family("Yang", 4, listOf(p1, p2, p3))
Assertions.assertThat(objectMapper.writeValueAsString(myFamily))
.isEqualTo("{\"[name]\":\"YangFamily\",\"[number]\":\"4people\",\"people\":[{\"name\":\"tomas\",\"age\":27},{\"name\":\"eil\",\"age\":47},{\"name\":\"json\",\"age\":22}]}")
}
@Test
fun deserialize() {
val json = "{\n" +
" \"name\": \"fam\",\n" +
" \"number\": 3,\n" +
" \"people\": [\n" +
" {\n" +
" \"name\": \"tomas\",\n" +
" \"age\": 27\n" +
" },\n" +
" {\n" +
" \"name\": \"eilen\",\n" +
" \"age\": 11\n" +
" }\n" +
" ]\n" +
"}"
val family = objectMapper.readValue(json, Family::class.java)
Assertions.assertThat(family.name).isEqualTo("fam Family")
Assertions.assertThat(family.number).isEqualTo(1003)
}
}
'TIL' 카테고리의 다른 글
TIL) 쉘 스크립트, linux screen 명령어 (0) | 2022.01.31 |
---|---|
TIL) pull or Merge 취소, SFTP 서버제공 사이트, CONTENT_DISPOSITION, @Async (0) | 2021.12.29 |
TIL) 터미널에서 sftp 접속, Jackson2HttpMessageConverter (0) | 2021.11.20 |
TIL) @JsonFormat/@JsonSerialize, Rest response 방식의 404, configureDefaultServletHandling (0) | 2021.11.18 |
TIL) ParameterNamesModule 이슈, 에너테이션 클래스에서 사용 가능한 타입, 전체 프로젝트 의존성 트리 (0) | 2021.11.12 |