본문 바로가기

Android

Gson vs kotlinx-serialization

Kotlin에서 Gson을 사용하다보면 느끼는 한계가 있다.

 

1. Not Null 한 변수에 null 값이 들어갈 수 있다는것 

null check를 컴파일 타임에 하지 않아 런타임에 오류발생 가능성이 생김 

kotlin의 null safety한 장점이 소용이 없어진다 

 

2. default value 적용되지 않는 문제 

통신데이터에 값이 없다던가 여타 이유로 데이터가 없는경우 기본값을 적용하고싶은 경우가 있다 하지만 gson을  사용하는 경우 converte 할 data class 의 프로퍼티에 default value 를 적용해두어도 막상 converte 하면 적용되지 않는 문제가 있다 

 

즉 Gson이 kotlin의 장점을 최대화 할수있을만틈 kotlin에 친화적이지 않다는 결론이 났다 (두둥)

 

해결책으로는 kotlinx-serialization을 들수 있다 

여러가지 옵션을 지정할 수 있는 annotation을 지원하고, 앞서말한 gson의 문제점을 모두 보완할 수 있다 

또한 reflection을 하지 않아 성능적으로 더 장점이 있다고..

 

비교를 위한 예제 코드) 

돌려보면 출력 결과를 볼수 있다 

 

[JsonConverterTestGson.kt]

fun main() {
    val testDataWithKey = """
        {
            "이름" : "moon",
            "계좌번호" : "12345678",
            "은행코드" : "",
            "거래내역" : [
                {"돈": "얼마"},
                {"돈": "얼마"}
            ]
        }
    """.trimIndent()

    val testDataWithoutKey = """
        {
            "이름" : "moon",
            "계좌번호" : "12345678",
            "거래내역" : [
                {"돈": "얼마"},
                {"돈": "얼마"}
            ]
        }
    """.trimIndent()

    val userDataWithKey = Gson().fromJson(testDataWithKey, UserData::class.java)
    println(Des1)
    println(userDataWithKey)
    println()

    val userDataWithoutKey = Gson().fromJson(testDataWithoutKey, UserData::class.java)
    println(Des2)
    println(userDataWithoutKey)
    println()

    println(Des3)

    /** 모든 통신 데이터를 nullable선언하여 검사 하는법 -> Bankcode 를 NULLable로 */
    userDataWithoutKey.bankCode?.let {
        for (i in 0 until it.length) { print("*") }
    }

    /** not null 로 했는데 실제로 NULL 이 오면 대응 불가 */
   //for (i in 0 until userDataWithoutKey.bankCode.length) { print("*") }

}

data class UserData(
    @SerializedName("이름")
    var name: String?,

    @SerializedName("계좌번호")
    var accountNumber : String?,

    @SerializedName("은행코드")
    var bankCode: String? = "088",

    @SerializedName("거래내역")
    var list: ArrayList<TransferInfo>
)

data class TransferInfo(
    @SerializedName("돈")
    var amount: String?,
)

val Des1 = "key는 있고 값은 공백인경우"
val Des2 = "key가 없는경우 (& default value 적용 안된당)"

val Des3 = "Bank code를 not null 형태로 선언했는뎅 실제로 값이 오지 않는다면 not null 한 변수지만 예외가 발생 따라서, Gson을 사용할경우 모든 entity를 nullable로 하여 검사해야한다"

 

[JsonConverterTestKotlinxSerialization.kt]

fun main() {
    val testDataWithKey = """
        {
            "이름" : "moon",
            "계좌번호" : "12345678",
            "은행코드" : "",
            "거래내역" : [
                {"돈": "얼마"},
                {"돈": "얼마"}
            ]
        }
    """.trimIndent()

    val testDataWithoutKey = """
        {
            "이름" : "moon",
            "계좌번호" : "12345678",
            "거래내역" : [
                {"돈": "얼마"},
                {"돈": "얼마"}
            ]
        }
    """.trimIndent()

    val notnullTestAccountNum = """
        {
            "이름" : "moon",
            "거래내역" : [
                {"돈": "얼마"},
                {"돈": "얼마"}
            ]
        }
    """.trimIndent()

    val customJson = Json {
        ignoreUnknownKeys = true  // class에 정의되지 않은 값들 무시 (반대는 dataclass 에 디폴트 ㄱㄱ)
    }

    println(Des4)
    val userDataWithKey = customJson.decodeFromString<UserData2>(testDataWithKey)
    println(userDataWithKey)
    println()

    println(Des5)
    val userDataWithoutKey = customJson.decodeFromString<UserData2>(testDataWithoutKey)
    println(userDataWithoutKey)
    println()

    println(Des6)
//    val notnullTest = customJson.decodeFromString<UserData2>(notnullTestAccountNum)
//    println(notnullTest)
    println()

    println(Des7)
}

@Serializable
data class UserData2(
    @SerialName("이름")
    var name: String?,

    @SerialName("계좌번호")
    var accountNumber : String?,

    @SerialName("은행코드")
    var bankCode: String = "088",

    @SerialName("거래내역")
    var list: ArrayList<TransferInfo2>
)

@Serializable
data class TransferInfo2(
    @SerialName("돈")
    var amount: String?,
)

val Des4 = "key는 있고 값은 공백인경우"
val Des5 = "key가 없는경우 default value 잘적용됨"
val Des6 = "변수에 default value 설정도 안되어있으면서 null 이 온경우 예외발생"

val Des7 = "정리) 기본값 정의되어있지 않은 변수에 값이 안들어오면 예외발생\n" +
        " 즉 gson처럼 null이면서 notnull 변수에 들어가는 문제는 발생하지않는다 (대신예외발생) \n" +
        " 따라서 서버의 값이 보장되어있지 않거나 크로스 체크를 할 필요가 있으면 기본값 설정 하기 \n" +
        " Nullable / NOT NULL 모두가능하기때문에 각 변수 원하는 타입으로 선언하고 기본값을 NULL / 특정 값 으로하면될듯"