GsonSafeParser 是一个 Kotlin 优先的 Android Gson 防御性解析库。它面向 Android 项目发布 AAR,解决接口字段类型不稳定导致的整棵 Bean 解析失败问题,并提供 Retrofit 接入、解析事件观测、契约报告、接入诊断和 Demo App 验证工具。
核心目标不是把错误数据悄悄吞掉,而是做到三件事:保住能继续解析的 Bean,留下能定位到字段的契约证据,并让生产环境可以用结构化事件持续观察后端返回漂移。
- 字段级安全兜底:对象、集合、Map、基础类型出现 JSON 类型错配时,只兜底当前字段,尽量保住外层 Bean。
- 默认回归 Gson:Safe Adapter 创建失败、配置不完整或遇到无法确认的类型时,默认交回 Gson 原生解析策略。
- Kotlin 友好:支持 Kotlin data class 默认值、reified API、
parseSafe<T>()和fromJsonSafe<T>()。 - Retrofit 接入:提供
GsonSafeConverterFactory,支持空响应策略和 raw JSON 捕获上限。 - 契约证据:通过
SafeParserEvent、TypeMismatchEvent、contractReport()和toBackendMarkdown()输出字段 path、期望形状、实际形状、兜底动作、客户端影响和后端修复建议。 - Demo App:内置 Android 示例应用,可粘贴业务 JSON,在真机上对比 GsonSafeParser 和原生 Gson 解析结果。
当前开箱默认配置:fallbackPolicy = FallbackPolicy.NullOnly、primitiveParsingPolicy = PrimitiveParsingPolicy.DelegateToGson、emptyResponsePolicy = EmptyResponsePolicy.DefaultValueForUnitOrVoidOnly、useJdkUnsafe = false、mapItemKeyPolicy = MapItemKeyPolicy.Hash。
固定行为:
| 场景 | 异常 JSON | 处理结果 |
|---|---|---|
Object 字段 |
[]、""、1 |
字段错形默认返回 null 或保留构造默认值,外层对象继续解析。 |
顶层 Object |
[]、""、1 |
顶层 JSON 不是对象时通常返回 null;不可恢复 Gson 异常会继续抛出。 |
String 字段 |
[]、{} |
字段读取失败时保留构造默认值;根级交回 Gson 原生 Adapter。 |
FallbackPolicy(默认:FallbackPolicy.NullOnly):
| 目标类型 | 异常 JSON | FallbackPolicy.NullOnly(默认) |
FallbackPolicy.Default |
|---|---|---|---|
| List / Set | {}、"" |
返回 null。 |
返回空集合。 |
| Map | []、"" |
返回 null。 |
返回空 Map。 |
PrimitiveParsingPolicy(默认:PrimitiveParsingPolicy.DelegateToGson):
| 目标类型 | 异常 JSON | PrimitiveParsingPolicy.DelegateToGson(默认) |
PrimitiveParsingPolicy.Safe |
|---|---|---|---|
| Int / Long / Boolean | {}、[]、"" |
交回 Gson 原生 Adapter。 | 使用安全基础值。 |
EmptyResponsePolicy(默认:EmptyResponsePolicy.DefaultValueForUnitOrVoidOnly):
| 场景 | 响应内容 | DefaultValueForUnitOrVoidOnly(默认) |
DefaultValue |
Null |
DelegateToGson |
|---|---|---|---|---|---|
Retrofit Unit / Void 空 body |
空响应体 | Unit 返回 Unit,Void 返回 null。 |
返回各自空值。 | 返回 null。 |
交回 Gson 原生 Converter。 |
| Retrofit 业务模型空 body | 空响应体 | 返回 null。 |
返回默认对象。 | 返回 null。 |
通常会得到 EOFException。 |
发布产物是 Android AAR,并使用 JDK 17 编译。AAR 会自动合并框架自身 consumer ProGuard 规则;业务 App 仍需要按 Android 混淆 配置自己的模型 keep 规则。老项目不需要第一天全量补 @SerializedName,建议先用宽范围包级 keep 保住 release 字段名、构造方法和 Kotlin 默认值能力,稳定后再逐步收窄。@SerializedName 只能固定 JSON 字段名,不能替代 Kotlin Metadata 和构造方法 keep。
版本号以徽章为准。普通 Gson 或手动持有 Gson 实例时,只依赖 core:
implementation("io.github.logan0817:gson-safe-parser-core:0.2.1") // 接入 GsonSafeParser 核心解析能力。如果项目使用 Retrofit,只依赖 retrofit 模块即可;它会传递带上 core:
implementation("io.github.logan0817:gson-safe-parser-retrofit:0.2.1") // 接入 Retrofit Converter 扩展,并自动带上 core。data class ApiResponse(
val code: Int = 0,
val data: User? = null
)
data class User(
val id: Long = 0L,
val name: String = ""
)
val json = """{"code":200,"data":[]}"""
val gson = GsonSafeParser.create() // 创建带安全解析能力的 Gson 实例。
val response = gson.fromJson(json, ApiResponse::class.java) // 使用安全 Gson 解析接口响应。原生 Gson 遇到 data 期望对象却收到 [] 时会抛异常;GsonSafeParser 会兜底 data 字段,并继续解析外层 code。
可恢复错形才会被兜底,例如 Object 字段收到数组、集合收到对象、整数字段收到越界值。JSON 语法错误、根级解析失败,以及无法被字段级兜底隔离的委托 Adapter 异常会继续抛出;字段级 Adapter 读取失败可能只影响当前字段并产生事件。调用方仍应按业务需要保留异常上报或外层兜底。
Kotlin 便捷 API:
val response = GsonSafeParser.fromJsonSafe<ApiResponse>(json) // 直接解析并返回业务对象。
val result = GsonSafeParser.parseSafe<ApiResponse>(json) // 解析并同时返回事件列表。
println(result.value)
println(result.events)
println(result.contractReport().toMarkdown())
println(result.contractReport().toBackendMarkdown())
println(result.contractReport().summary.warningCount)
println(result.contractReport().toStructuredRows().firstOrNull()?.stableKey)如果你要高频重复解析同一套接口,先创建一次可复用 Parser 更合适:
val parser = GsonSafeParser.parser(config) // 只创建一次安全 Parser,后续反复复用同一个 Gson。
val value = parser.fromJsonSafe<ApiResponse>(json)
val result = parser.parseSafe<ApiResponse>(json)val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonSafeConverterFactory.create()) // 注册 GsonSafeParser 的响应转换器。
.build()如果需要自定义空响应或观测策略:
val config = SafeParserConfig.production(
observerPolicy = SafeObserverPolicy(
onEvent = { event -> println(event) } // 接收并上报解析事件。
)
)
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonSafeConverterFactory.create(config)) // 使用自定义配置注册转换器。
.build()如果项目里已经统一维护了 Gson,同时还想保留 Retrofit 层的空响应、rawJson 和事件策略,可以使用:
val config = SafeParserConfig.debug() // 既控制 Safe Gson,也控制 Retrofit 层的空响应和观测策略。
val gson = GsonBuilder()
.serializeNulls()
.enableSafeParser(config) // 把同一份 SafeParserConfig 注册到 Gson。
.create()
val retrofit = Retrofit.Builder()
.baseUrl("https://example.com/")
.addConverterFactory(GsonSafeConverterFactory.create(gson, config)) // 同时复用已有 Gson 和 Retrofit 层 SafeParserConfig。
.build()GsonSafeConverterFactory.create(gson) 和 create(gson, config) 不会修改传入的 Gson;如果需要字段级安全解析,应该在创建这份 Gson 前,对同一个 GsonBuilder 调用 .enableSafeParser(config)。
val config = SafeParserConfig(
fallbackPolicy = FallbackPolicy.NullOnly, // 字段错配时返回 null 或保留构造默认值。
emptyResponsePolicy = EmptyResponsePolicy.DefaultValueForUnitOrVoidOnly, // Retrofit 空响应只为 Unit/Void 返回空值。
primitiveParsingPolicy = PrimitiveParsingPolicy.DelegateToGson, // 基础类型交回 Gson 原生 Adapter。
skippedPlatformTypePrefixes = setOf("android."), // 跳过 Android 平台类型,避免反射系统对象;不要把业务模型包名前缀放这里。
nullValuePolicy = NullValuePolicy.WriteExplicitNulls, // 显式 JSON null 只写入 nullable 字段。
mapItemKeyPolicy = MapItemKeyPolicy.Hash, // Map item 事件默认输出稳定哈希。
captureRawJsonInCallbacks = false, // 线上默认不在事件中携带原始 JSON。
maxRawJsonCaptureBytes = 1024 * 1024, // 限制 raw JSON 最大捕获体积为 1 MiB。
onEvent = { event -> println(event) }, // 监听统一解析事件。
onTypeMismatch = { event ->
println("${event.path}: ${event.actualToken} -> ${event.expectedType}")
} // 输出字段路径、实际 token 和期望类型。
)预设配置:
val production = SafeParserConfig.production() // 创建线上默认配置。
val debug = SafeParserConfig.debug() // 创建联调配置,默认开启 raw JSON 捕获。
val lowInterference = SafeParserConfig.lowInterference() // 创建低误伤配置,行为更接近原生 Gson。production()适合正式上线时作为默认配置。它使用契约优先读策略和事件观测,但默认不把整段 raw JSON 挂到回调里,这样排障信息够用,又不容易把大响应体、敏感字段或额外内存开销长期带到线上链路里。debug()适合联调、测试和接口排障。它和production()使用同一套契约优先读策略,会把 raw JSON 一起带进事件回调,方便你直接看到“后端到底返回了什么,哪个字段开始错位”,但同时会加默认体积上限,避免把超大响应整段塞进日志。lowInterference()适合灰度接入,或者你现在最担心的是“新库会不会改动我原来太多解析结果”。它具体会做几件事:字段、集合、Map 遇到整体类型错配时优先返回null,而不是主动造空集合、空 Map 或默认对象;Int、Boolean、String这类基础类型交回 Gson 原生 Adapter 处理;Retrofit 空响应默认返回null;对象构造时不使用 JDK Unsafe 绕过构造函数。这样它仍然会记录错配事件和保住能继续解析的外层结构,但不会过早把错误数据改成安全默认值。
@SafeParseDelegateToGson // 让这个类型直接使用 Gson 原生 Adapter。
class StrictModel
data class PageState(
@field:SafeParseSkip // 告诉 Safe Reflective 跳过这个字段。
val runtimeCache: Any? = null
)@SafeParseDelegateToGson标在类上,表示该类型直接交给 Gson 原生 Adapter。@SafeParseSkip标在字段上,表示 Safe Reflective 不读写该字段,适合运行时状态、缓存字段或平台对象。
仓库内置 demo-app,用于真机验证库能力:
./gradlew :demo-app:assembleDebug # 构建 debug 版本 Demo App。
./gradlew :demo-app:installDebug # 把 debug Demo App 安装到已连接设备。
adb shell am start -n io.github.logan.gsonsafeparser.demo/.MainActivity # 启动 Demo App 首页。Demo App 支持内置用例和用户自定义 JSON。你可以把接口返回直接粘贴进去,对比 GsonSafeParser 和原生 Gson 的解析结果、事件流和接入建议。
建议阅读顺序:先看 快速开始,再看 配置说明 和 错形能力矩阵;如果是 Android release 接入,再补看 Android 混淆。
- 快速开始:安装、普通 Gson、Retrofit、Kotlin API 和 CI 自检。
- 错形能力矩阵:对象、集合、Map、基础类型、Kotlin 默认值、Retrofit 空响应和 raw JSON 捕获的处理范围。
- 配置说明:配置项、预设策略、事件流、注解和默认行为。
- Android 混淆:新项目接入、老项目快速接入、R8 fullMode 选择和 release 验证。
- Demo App:真机测试方式、页面说明和用户 JSON 验证入口。
- 排障指南:空响应、raw JSON、Adapter 创建失败、平台对象和业务协议问题。
- 发布清单:0.2.1 发版前的 AAR、混淆、文档版本和 Maven 本地产物检查。
- 0.2.1 发布说明:契约优先默认行为、Breaking Changes 和发布前注意事项。
GsonSafeParser 是 Gson 的增强层,不是新的 JSON 协议解释器。它可以降低解析崩溃概率,并告诉你哪个字段出现了类型错配,但不会证明后端契约是正确的。
当问题超出当前库的处理边界时,默认原则是交回 Gson 原生链路处理。遇到 Gson 版本差异、混淆导致运行时信息缺失、用户配置不完整或 Safe Adapter 创建失败时,库本身不应成为新的崩溃来源;JSON 语法错误、根级解析失败,以及无法被字段级兜底隔离的委托 Adapter 异常会继续抛出,不会被 parseSafe 静默吞掉。
GsonSafeParser 是独立维护的 Kotlin 开源项目。当前仓库代码以维护者主导、AI 辅助重构的方式持续演进,最终结果由维护者审核、调整并验证。AI 使用情况单独说明如下:
- ChatGPT Codex:用于重构、测试补强、文档整理和自查。
- DeepSeek DeepSeek-V4-Pro:用于重构辅助、文档润色和问题复核。
项目在设计、问题场景梳理、README 审阅和 issue 自查阶段参考过公开项目 getActivity/GsonFactory,相关许可证、原版权声明和更完整的 AI 透明说明见 NOTICE。
Apache License 2.0