求助:将含不规则坐标的API响应转为Kotlin Data Class
解决方案:自定义JSON反序列化处理动态无序坐标字段
1. 重构数据类结构
先替换原空的MapCoordinate,改成包含有序坐标列表的结构,同时定义单个坐标的实体类:
// 单个坐标点的实体类 data class SingleCoordinate( val x: String, val y: String, val lat: String, val lng: String ) // 替换原空MapCoordinate,存储排序后的坐标列表 data class MapCoordinate( val coordinates: List<SingleCoordinate> )
2. 自定义Gson反序列化器(Android常用方案)
针对后端返回的xN/yN/latN/lngN动态无序字段,手动提取数字后缀、分组排序后组装成有序列表:
class MapCoordinateDeserializer : JsonDeserializer<MapCoordinate> { override fun deserialize( json: JsonElement, typeOfT: Type?, context: JsonDeserializationContext? ): MapCoordinate { val jsonObject = json.asJsonObject // 按数字后缀分组存储每个坐标的四个属性 val coordinateGroups = mutableMapOf<Int, MutableMap<String, String>>() jsonObject.entrySet().forEach { entry -> val key = entry.key val value = entry.value.asString // 匹配字段名的前缀(x/y/lat/lng)和数字后缀 val matchResult = Regex("([a-z]+)(\\d+)").find(key) matchResult?.let { val prefix = it.groupValues[1] val index = it.groupValues[2].toInt() // 初始化当前索引的分组容器 if (!coordinateGroups.containsKey(index)) { coordinateGroups[index] = mutableMapOf() } coordinateGroups[index]?.put(prefix, value) } } // 按数字索引排序,组装成完整坐标列表 val sortedCoordinates = coordinateGroups.keys.sorted().mapNotNull { index -> val group = coordinateGroups[index] // 确保四个属性齐全再组装,避免缺失字段导致异常 if (group?.containsAll(listOf("x", "y", "lat", "lng")) == true) { SingleCoordinate( x = group["x"]!!, y = group["y"]!!, lat = group["lat"]!!, lng = group["lng"]!! ) } else { // 可根据需求调整:抛出异常/跳过该坐标/给默认值 null } } return MapCoordinate(sortedCoordinates) } }
3. 注册解析器并使用
创建Gson实例时注册自定义解析器,即可正常解析API响应:
val gson = GsonBuilder() .registerTypeAdapter(MapCoordinate::class.java, MapCoordinateDeserializer()) .create() // 解析API返回的Response<MapLabels> val response = gson.fromJson(apiResponseString, Response::class.java)
4. Moshi适配器替代方案(若项目用Moshi)
如果项目使用Moshi作为JSON解析库,可编写对应适配器:
class MapCoordinateAdapter { @FromJson fun fromJson(json: JsonObject): MapCoordinate { val coordinateGroups = mutableMapOf<Int, MutableMap<String, String>>() json.keys.forEach { key -> val value = json[key]?.asString ?: return@forEach val matchResult = Regex("([a-z]+)(\\d+)").find(key) matchResult?.let { val prefix = it.groupValues[1] val index = it.groupValues[2].toInt() if (!coordinateGroups.containsKey(index)) { coordinateGroups[index] = mutableMapOf() } coordinateGroups[index]?.put(prefix, value) } } val sortedCoordinates = coordinateGroups.keys.sorted().mapNotNull { index -> val group = coordinateGroups[index] if (group?.containsAll(listOf("x", "y", "lat", "lng")) == true) { SingleCoordinate( x = group["x"]!!, y = group["y"]!!, lat = group["lat"]!!, lng = group["lng"]!! ) } else { null } } return MapCoordinate(sortedCoordinates) } }
注册并使用:
val moshi = Moshi.Builder() .add(MapCoordinateAdapter()) .build() val adapter = moshi.adapter(Response::class.java) val response = adapter.fromJson(apiResponseString)
核心逻辑说明
- 正则表达式
([a-z]+)(\\d+)精准匹配字段前缀和数字后缀,彻底解决超过9个点时的排序混乱问题(比如x10不会排在x2前面)。 - 通过数字索引分组+排序,保证最终坐标列表的顺序与标注点的绘制顺序一致。
- 增加了属性完整性校验,避免后端返回缺失字段导致的解析崩溃。
内容的提问来源于stack exchange,提问作者AmirHossin TaghiPour
相关产品推荐
相关产品推荐

