Kotlin에서 reflection을 사용하여 JSON deserialization 해보기

JhoonP·2023년 3월 19일

Kotlin에서 reflection을 사용하여 JSON serialization 해보기에 이어서, deserialization을 구현해본다.


  1. 변환을 위해서는 변환할 JSON Map 객체와 변환할 타입형인 KClass가 필요하다. 함수 parameter로 KClass type을 사용해 변환할 class type을 받는다.
fun <TR: Any> deserializeMap(type: KClass<TR>, map: Map<String, Any>): TR
  1. KClass.primaryConstructor로 기본 생성자 정보를 가져올 수 있다. 생성자의 parameter 정보들을 KParameter type으로 가져와 parameterInfo에 저장할 것이다
val parameterInfo = mutableMapOf<KParameter, Any?>()
type.primaryConstructor?.parameters?.forEach {
	...
}
  1. KClass.primaryConstructor.name으로 생성자 parameter의 이름, 즉 member value를 가져올 수 있다. JSON Map에서 member value를 key로 가진 item을 찾는다
val item = map[it.name]
if(item is JSONObject) {
	//	Performing recursive transformations
} else if(item is JSONArray) {
    //	Deserialize each item in an array and store it in a List
} else if(item != null) {
    //	Ignore
} else {
    //	Ignore
}
  1. item이 data type(String, int 등)이 아닌 object type일 경우 한 번 더 deserialize 해야한다. parameterInfoKParamter와 deserialize한 value 값을 저장한다.
if(item is JSONObject) {
	//	Performing recursive transformations
    parameterInfo[it] = deserializeMap(it.type.classifier as KClass<*>, jsonObjectToMap(item))
}
  1. item이 Array type인 경우 Kotlin List으로 변환해야 한다. 각 array item들 순회하면서 deserialize를 수행한 후 List에 저장한다.
    Array는 Generic class이기 때문에 타입을 추출하여 deserialize 하는데 넘겨줘야 한다.
} else if(item is JSONArray) {
    val list = mutableListOf<Any?>()
    val template = it.type.arguments[0].type!!.classifier
    for(subItem in jsonListToList(item)) {
        if(subItem is JSONObject) {
            list.add(deserializeMap(template as KClass<*>, jsonObjectToMap(subItem)))
        }
    }
    parameterInfo[it] = list
}
  1. 그 외 값들에 대해서는 그대로 사용하면 된다.
} else if(item != null) {
    parameterInfo[it] = item
} else {
    parameterInfo[it] = null
}
  1. 이렇게 얻은 List<KParameter, Any?> 값을 생성자의 parameter로 넘겨 실행하면 deserialize된 instance를 얻을 수 있다.
return type.primaryConstructor?.callBy(parameterInfo)!!

결과

fun deserialize(): Any {
    val jsonString = """
        {
         "age": 30,
         "height": 171.2,
         "name": "John Doe",
         "subWorkplace": [
          {
           "isLargeEnterprise": true,
           "name": "Google"
          }
         ],
         "workplace": {
          "isLargeEnterprise": true,
          "name": "Google"
         }
        }
    """.trimIndent()
    
    val result = JJson().deserializeMap(Person::class, JJson.jsonStringToMap(jsonString))
    Log.d(TAG, "Deserialize result: $result")
    return result
}
Deserialize result: Person(name=John Doe, age=30, height=171.2, workplace=Company(name=Google, isLargeEnterprise=true, numOfEmployees=null), subWorkplace=[Company(name=Google, isLargeEnterprise=true, numOfEmployees=null)])
profile
배울게 끝이 없네 끝이 없어

0개의 댓글