I want to make Gson deserializing numbers strictly, for example when Gson receives "123"
and needs to deserialize it to Int
, it must throw an exception and not automatically convert the string to number.
The goal:
val str1 = """{"key":"123"}"""
Gson().fromJson(str1, intMapType) // throws an Exception
val str2 = """{"key":123}"""
Gson().fromJson(str2, intMapType) // returns valid Map<String, Int>
I have already tried to register cursom type adapters for Int
and String
:
private class StrictIntDeserializer : JsonDeserializer<Int> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Int {
if (!json.isJsonPrimitive || !json.asJsonPrimitive.isNumber) {
throw JsonParseException("Expected Int but got: $json")
}
return json.asInt
}
}
private class StrictStringDeserializer : JsonDeserializer<String> {
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): String {
if (!json.isJsonPrimitive || !json.asJsonPrimitive.isString) {
throw JsonParseException("Expected String but got: $json")
}
return json.asString
}
}
private val gson = GsonBuilder()
.registerTypeHierarchyAdapter(Int::class.java, StrictIntDeserializer())
.registerTypeHierarchyAdapter(String::class.java, StrictStringDeserializer())
.create()
But this does not work.
The problem was that Gson is a Java library and all the types it uses are Java primitives, not Kotlin (e.g. Kotlin's Int
= Java's java.lang.Integer
). So it was necessary to register adapters specifically for the Java equivalents of Kotlin classes:
Instead of
.registerTypeHierarchyAdapter(Int::class.java, StrictIntDeserializer())
we need
.registerTypeHierarchyAdapter(java.lang.Integer::class.java, StrictIntDeserializer())
Otherwise Gson simply never accesses this adapter.