如何在Gson中启用严格类型校验?
问题:Gson自动转换类型不匹配的JSON字段,如何禁用这种宽松行为?
我在用Gson解析JSON时发现一个意外行为:当POJO的String字段对应JSON里的number值,或者Int字段对应JSON里的string值时,Gson会自动完成类型转换,不会抛出异常。以下测试用例可以复现这个问题:
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") // 测试通过 assertThat(parsed.testNumber).isEqualTo(1) // 测试通过 // 原本预期类型不匹配时抛出异常,但实际没有 }
我只找到GsonBuilder.setLenient()方法,但它和这个问题无关,请问有没有办法修改Gson的这种行为?
解决方案
Gson默认的宽松类型转换是为了兼容不规范的JSON数据,但如果需要严格的类型校验,可以通过自定义TypeAdapter来实现,强制检查JSON原始类型与POJO字段类型是否匹配:
1. 为单个类型编写严格的TypeAdapter
针对需要严格校验的类型(比如String、Int),编写对应的TypeAdapter,解析时判断JSON token类型,不匹配则抛出异常:
String类型的严格适配器
class StrictStringAdapter : TypeAdapter<String>() { override fun write(out: JsonWriter, value: String?) { out.value(value) } override fun read(`in`: JsonReader): String { if (`in`.peek() != JsonToken.STRING) { throw JsonSyntaxException("预期String类型,但实际是${`in`.peek()},路径:${`in`.path}") } return `in`.nextString() } }
Int类型的严格适配器
class StrictIntAdapter : TypeAdapter<Int>() { override fun write(out: JsonWriter, value: Int?) { out.value(value) } override fun read(`in`: JsonReader): Int { if (`in`.peek() != JsonToken.NUMBER) { throw JsonSyntaxException("预期Number类型,但实际是${`in`.peek()},路径:${`in`.path}") } return `in`.nextInt() } }
2. 注册适配器到GsonBuilder
将自定义的TypeAdapter注册到Gson实例中,替换默认的类型转换逻辑:
val gson = GsonBuilder() .registerTypeAdapter(String::class.java, StrictStringAdapter()) .registerTypeAdapter(Int::class.java, StrictIntAdapter()) .create()
此时再运行原测试用例,Gson会抛出JsonSyntaxException,符合类型不匹配时抛出异常的预期。
3. 批量处理多个类型(可选)
如果需要对多个基本类型都做严格校验,可以编写TypeAdapterFactory统一管理:
class StrictTypeAdapterFactory : TypeAdapterFactory { override fun <T> create(gson: Gson, type: TypeToken<T>): TypeAdapter<T>? { val rawType = type.rawType return when { rawType == String::class.java -> StrictStringAdapter() as TypeAdapter<T> rawType == Int::class.java -> StrictIntAdapter() as TypeAdapter<T> // 可扩展添加Long、Double等其他需要严格校验的类型 else -> null } } }
注册方式:
val gson = GsonBuilder() .registerTypeAdapterFactory(StrictTypeAdapterFactory()) .create()
内容的提问来源于stack exchange,提问作者marcinm
相关产品推荐
相关产品推荐

