I have to deserialize 0
to false
and 1
to true
.
I've created this class:
class IntBooleanDeserializer : JsonDeserializer<Boolean?> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Boolean? {
json?.let {
return json.asInt == 1
}
return null
}
}
And registered it:
private val gson = GsonBuilder()
.registerTypeAdapter(Boolean::class.java, IntBooleanDeserializer())
.create()
And created test class for this:
data class BooleanClass(val value: Boolean?)
And then:
gson.fromJson("{\"value\": 0}", BooleanClass::class.java)
This code throws exception:
com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected a boolean but was NUMBER at line 1 column 12 path $.value
Seems that Gson does not use my deserializer for Boolean?
, but successfully uses other custom deserializers for other types (for example, for enums).
Why?
You're registering the deserializer for Boolean::class.java
which is boolean
while the type you require actually is Boolean?
translating to java.lang.Boolean
.
To get java.lang.Boolean
you have to use Boolean::class.javaObjectType
for the registration.
You'd also find the same behavior for all primitive Java types.
Inspired by @tynn's answer and this post. I design two TypeAdater
s to cover Boolean
and Boolean?
type in Kotlin.
private val TRUE_STRINGS: Array<String> = arrayOf("true", "1")
class BooleanObjectTypeAdapter : JsonDeserializer<Boolean?>, JsonSerializer<Boolean?> {
override fun serialize(src: Boolean?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
return if (src == null) {
JsonNull.INSTANCE
} else {
JsonPrimitive(src)
}
}
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Boolean? {
if (json == null || json.isJsonNull) {
return null
}
return when {
json !is JsonPrimitive -> false
json.isBoolean -> json.asBoolean
json.isNumber -> json.asNumber.toInt() == 1
json.isString -> TRUE_STRINGS.contains(json.asString.lowercase())
else -> false
}
}
}
class BooleanPrimitiveTypeAdapter : JsonDeserializer<Boolean>, JsonSerializer<Boolean> {
override fun serialize(src: Boolean?, typeOfSrc: Type?, context: JsonSerializationContext?): JsonElement {
return if (src == null) {
JsonPrimitive(false)
} else {
JsonPrimitive(src)
}
}
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): Boolean {
if (json == null || json.isJsonNull) {
return false
}
return when {
json !is JsonPrimitive -> false
json.isBoolean -> json.asBoolean
json.isNumber -> json.asNumber.toInt() == 1
json.isString -> TRUE_STRINGS.contains(json.asString.lowercase())
else -> false
}
}
}
You can register these adapters by
GsonBuilder()
.registerTypeAdapter(Boolean::class.javaObjectType, BooleanObjectTypeAdapter())
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanPrimitiveTypeAdapter())
.create()
I also create a gist to keep these and write tests.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With