I am using Gson to parse JSON. What surprised me is that if I have a field of type String
in my POJO class and I have a number
in JSON it’s parsed correctly without throwing an exception. Similarly the other way – I wrote this test to illustrate:
data class Parsed(
val testString: String,
val testNumber: Int,
)
@Test
fun `test gson type parsing`() {
val gson = Gson()
val json = "{ "testString" : 1, "testNumber" : "1"}"
val parsed = gson.fromJson(json, Parsed::class.java)
assertThat(parsed.testString).isEqualTo("1") // passes
assertThat(parsed.testNumber).isEqualTo(1) // passes
// expected an exception because number is parsed into a string field and string is parsed into number
}
Is there any way to change this behavior? (Only found setLenient()
on GsonBuilder
but it’s not related to this)
2
Answers
I don’t think you can turn of type coercion in gson. Your best bet would probably be to write a custom deserializer that prevents this.
That being said, contemplate not using gson in kotlin code as it completely circumvents the null safety of the language. You are better of using a serialization framework that is kotlin aware, such as jackson (with kotlin plugin) or kotlinx-serialization.
Aditionally it seems like jackson allows to turn this specific type coercion off, using this flag: https://fasterxml.github.io/jackson-databind/javadoc/2.9/com/fasterxml/jackson/databind/MapperFeature.html#ALLOW_COERCION_OF_SCALARS
Kotlinx-serialization seems to not have such an option: https://github.com/Kotlin/kotlinx.serialization/issues/1042
There are (at least) the following options:
Subclass
JsonReader
and override its number reading methods (such asnextInt()
) andnextString()
to first callpeek()
and verify that the JSON data has the correct type.Disadvantages:
JsonReader
JsonReader
you would break these adapters because your subclass cannot tell the adapters apartJsonElement
(and possibly performs modifications or checks on it) and then deserializes that as desired typeImplement a custom
TypeAdapterFactory
which wraps adapters forString
andNumber
and subclasses to first callJsonReader.peek()
to make sure the JSON data has the correct typeDisadvantages:
StringBuilder
adapterSince you are concerned with strict validation, it might be good to point out that Gson by default performs lenient parsing (see Troubleshooting Guide), and even in its ‘strict mode’ it slightly deviates from the JSON specification, see this issue.