Gson反序列化关联对象列表:如何规避复杂自定义反序列化器?
嘿,这个问题我刚好有靠谱的解法!完全不用写那种臃肿难维护的自定义反序列化器,不管你用Gson还是Jackson,用DTO中转+逻辑转换的方式就能轻松搞定,咱们一步步来捋:
核心思路
先把JSON反序列化成和原始结构完全匹配的临时DTO(数据传输对象),避开自定义反序列化的复杂逻辑;再把DTO转换成你需要的实体类,同时处理你的两个核心需求——最后按类型分类存储即可。这种方式代码清晰、易维护,比写庞大的JsonDeserializer舒服太多。
用Gson实现的方案
1. 定义匹配JSON结构的临时DTO
先写几个和JSON字段一一对应的DTO类,让Gson自动完成基础反序列化:
// 顶层DTO,对应整个JSON结构 data class RawData( val categories: List<RawCategory>, val items: List<RawItem> ) // 对应JSON里的category节点 data class RawCategory( val id: String, val name: String, val type1: String?, val type2: String? ) // 对应JSON里的item节点 data class RawItem( val id: String, val name: String, val type1: String?, val type2: String?, val categoryIds: List<String> )
2. 转换处理核心需求
接下来把DTO转换成你的目标实体类,同时处理两个核心规则:
val gson = Gson() // 第一步:把JSON转成DTO val rawData = gson.fromJson(yourJsonString, RawData::class.java) // 处理需求1:一个Category节点有type1/type2就生成两个同id的Category实例 val allCategories = mutableListOf<Category>() rawData.categories.forEach { rawCat -> // 收集所有非空的type字段值 val typeStrings = mutableListOf<String>() rawCat.type1?.let { typeStrings.add(it) } rawCat.type2?.let { typeStrings.add(it) } // 每个type对应一个Category实例 typeStrings.forEach { typeStr -> val categoryType = CategoryType.valueOf(typeStr) // 注意这里items用MutableList,方便后续添加Item allCategories.add(Category(rawCat.name, rawCat.id, categoryType, mutableListOf())) } } // 处理需求2:把符合条件的Item实例加入对应Category的items列表 rawData.items.forEach { rawItem -> // 收集Item的所有type字段值 val itemTypeStrings = mutableListOf<String>() rawItem.type1?.let { itemTypeStrings.add(it) } rawItem.type2?.let { itemTypeStrings.add(it) } // 创建Item实例(这里假设Item的type取第一个非空的type值,你可以根据需求调整) val item = Item( name = rawItem.name, id = rawItem.id, type = CategoryType.valueOf(itemTypeStrings.firstOrNull() ?: throw IllegalArgumentException("Item ${rawItem.id} has no valid type")), data = null, categoryIds = rawItem.categoryIds.toTypedArray() ) // 找到所有符合条件的Category:type匹配 + id在Item的categoryIds里 allCategories.filter { cat -> itemTypeStrings.contains(cat.type.name) && rawItem.categoryIds.contains(cat.id) }.forEach { matchingCat -> // 把同一个Item实例加入匹配的Category的items列表 (matchingCat.items as MutableList<Item>).add(item) } } // 最后按CategoryType分类存储 val typedCategories = allCategories.groupBy { it.type } // 比如获取TYPE_A的Category列表 val typedAsTYPE_A = typedCategories[CategoryType.TYPE_A] ?: emptyList()
用Jackson实现的方案
Jackson的思路和Gson完全一致,只是反序列化的API略有不同:
1. 定义同样的临时DTO类
和上面Gson的DTO完全一样,就不重复贴了。
2. 反序列化+逻辑转换
// 初始化Jackson的ObjectMapper(记得注册Kotlin模块) val objectMapper = ObjectMapper().registerModule(KotlinModule()) // 转成DTO val rawData = objectMapper.readValue(yourJsonString, RawData::class.java) // 以下的Category生成、Item关联逻辑和Gson方案完全相同 val allCategories = mutableListOf<Category>() rawData.categories.forEach { rawCat -> val typeStrings = mutableListOf<String>() rawCat.type1?.let { typeStrings.add(it) } rawCat.type2?.let { typeStrings.add(it) } typeStrings.forEach { typeStr -> val categoryType = CategoryType.valueOf(typeStr) allCategories.add(Category(rawCat.name, rawCat.id, categoryType, mutableListOf())) } } rawData.items.forEach { rawItem -> val itemTypeStrings = mutableListOf<String>() rawItem.type1?.let { itemTypeStrings.add(it) } rawItem.type2?.let { itemTypeStrings.add(it) } val item = Item( rawItem.name, rawItem.id, type = CategoryType.valueOf(itemTypeStrings.firstOrNull() ?: throw IllegalArgumentException("Item ${rawItem.id} has no valid type")), data = null, categoryIds = rawItem.categoryIds.toTypedArray() ) allCategories.filter { cat -> itemTypeStrings.contains(cat.type.name) && rawItem.categoryIds.contains(cat.id) }.forEach { matchingCat -> (matchingCat.items as MutableList<Item>).add(item) } } // 按类型分类 val typedCategories = allCategories.groupBy { it.type } val typedAsTYPE_A = typedCategories[CategoryType.TYPE_A] ?: emptyList()
为什么推荐这种方式?
比起写庞大的自定义JsonDeserializer,这种DTO中转的方式有几个明显优势:
- 可读性强:逻辑拆分清晰,谁看都能懂,出问题好排查
- 低耦合:不用和Gson/Jackson的内部反序列化细节绑定,后续换框架也容易
- 易扩展:如果后续需求变更,直接修改转换逻辑就行,不用动反序列化的底层代码
不管你现在用的是Gson还是Jackson,都能完美实现你的需求,完全没必要特意切换框架~
内容的提问来源于stack exchange,提问作者Rawnald Gregory Erickson
相关产品推荐
相关产品推荐

