You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:42:44