Home > Mobile >  How to encode/decode CHAR type as number with GSON with Kotlin?
How to encode/decode CHAR type as number with GSON with Kotlin?

Time:11-04

How i can make a custom encode/decode for CHAR type using GSON library with Kotlin?

My current code don't work and it don't stop on debug.

Appear that type don't match.

My code:

data class Test(
    var typeChar: Char,
)
private fun createGson(): Gson {
    val builder = GsonBuilder()

    // char
    builder.registerTypeAdapter(
        Char::class.java,
        JsonDeserializer<Any?> { json, _, _ ->
            try {
                Char(json.asJsonPrimitive.asInt)
            } catch (e: Exception) {
                // ignore
            }

            null
        },
    )

    builder.registerTypeAdapter(
        Char::class.java,
        com.google.gson.JsonSerializer<Char?> { value, _, _ ->
            JsonPrimitive(value.code)
        },
    )

    return builder.create()
}

Can anyone help me?

I tried a sample code above and search on github gson repository for infos.

CodePudding user response:

The problem is that there are two different types representing characters in Java, char, the primitive type, and Character, the wrapper. The Kotlin type Char can be mapped to either, depending on whether the field is nullable or not. As a result, you have to map both types. This is also mentioned by docs for the registerTypeAdapter method,

This registers the type specified and no other types: you must manually register related types! For example, applications registering boolean.class should also register Boolean.class.

In your case, this means that you need to register your serializer and deserializer for both Char::class.java and Character::class.java:

private fun createGson(): Gson {
    val builder = GsonBuilder()

    builder.registerTypeAdapter(
        Char::class.java,
        JsonDeserializer<Any?> { json, _, _ ->
            try {
                Char(json.asJsonPrimitive.asInt)
            } catch (e: Exception) {
                null
            }
        },
    )
    builder.registerTypeAdapter(
        Character::class.java,
        JsonDeserializer<Any?> { json, _, _ ->
            try {
                Char(json.asJsonPrimitive.asInt)
            } catch (e: Exception) {
                null
            }
        },
    )
    builder.registerTypeAdapter(
        Char::class.java,
        JsonSerializer<Char?> { value, _, _ ->
            JsonPrimitive(value.code)
        },
    )
    builder.registerTypeAdapter(
        Character::class.java,
        JsonSerializer<Char?> { value, _, _ ->
            JsonPrimitive(value.code)
        },
    )

    return builder.create()
}

Also, note the implementation of JsonDeserializer, the null has to be inside the catch, otherwise, it would always return null.

With some refactoring, we can simplify this in the following way:

private fun createGson(): Gson {
    val builder = GsonBuilder()

    val customCharSerializer = object : JsonDeserializer<Any?>, JsonSerializer<Char?> {
        override fun deserialize(json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext?): Any? {
            return try {
                Char(json.asJsonPrimitive.asInt)
            } catch (e: Exception) {
                null
            }
        }

        override fun serialize(src: Char?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement? {
            return src?.let { JsonPrimitive(it.code) }
        }
    }

    builder.registerTypeAdapter(Char::class.java, customCharSerializer,)
    builder.registerTypeAdapter(Character::class.java, customCharSerializer,)

    return builder.create()
}
  • Related