Swift Json Decoder:如何获取未解码的嵌套GeoJSON字符串
解决方法
问题核心是服务器返回的geojson字段是JSON对象(字典/数组),但你直接尝试解码成String,解码器自然会抛出类型不匹配的错误。要拿到原始JSON字符串,得先把这个JSON对象解码成可编码的类型,再重新编码回Data,最后转成字符串。
步骤1:实现通用的AnyCodable类型
Swift默认不支持[String: Any]这类动态类型的Codable解码,所以需要自己写一个兼容所有JSON类型的结构体:
struct AnyCodable: Codable { let value: Any init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let bool = try? container.decode(Bool.self) { value = bool } else if let int = try? container.decode(Int.self) { value = int } else if let string = try? container.decode(String.self) { value = string } else if let double = try? container.decode(Double.self) { value = double } else if let array = try? container.decode([AnyCodable].self) { value = array.map { $0.value } } else if let dict = try? container.decode([String: AnyCodable].self) { value = dict.mapValues { $0.value } } else { throw DecodingError.dataCorruptedError(in: container, debugDescription: "不支持的JSON类型") } } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch value { case let bool as Bool: try container.encode(bool) case let int as Int: try container.encode(int) case let string as String: try container.encode(string) case let double as Double: try container.encode(double) case let array as [Any]: try container.encode(array.map { AnyCodable(value: $0) }) case let dict as [String: Any]: try container.encode(dict.mapValues { AnyCodable(value: $0) }) default: throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "不支持的JSON类型")) } } }
步骤2:修改解码逻辑
在init(from decoder: Decoder)里,用AnyCodable解码geojson字段,再重新编码成字符串:
init(from decoder: Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) id_s = try container.decode(String.self, forKey: .id_s) name = try container.decode(String.self, forKey: .name) description = try container.decode(String.self, forKey: .description) area_type = try container.decode(String.self, forKey: .area_type) active = String(try container.decode(Bool.self, forKey: .active)) danger = String(try container.decode(Bool.self, forKey: .danger)) scheduled_from = try container.decodeIfPresent(String.self, forKey: .scheduled_from) scheduled_to = try container.decodeIfPresent(String.self, forKey: .scheduled_to) // 处理geojson字段 let geojsonAny = try container.decode(AnyCodable.self, forKey: .geojson) do { let geojsonData = try JSONEncoder().encode(geojsonAny) geojsonString = String(data: geojsonData, encoding: .utf8) ?? "" } catch { // 编码失败时的兜底处理,比如设为空字符串或者抛出错误 geojsonString = "" // 如果需要严格校验,可抛出错误:throw error } }
替代方案(类型安全场景)
如果服务器返回的GeoJSON结构固定,你可以先定义对应的模型(比如Feature、Geometry),解码成模型后再转成字符串:
// 示例GeoJSON Feature模型 struct GeoJSONFeature: Codable { let type: String let properties: [String: AnyCodable] let geometry: GeoJSONGeometry } struct GeoJSONGeometry: Codable { let type: String let coordinates: [[Double]] } // 解码时使用 let geojsonModel = try container.decode(GeoJSONFeature.self, forKey: .geojson) let geojsonData = try JSONEncoder().encode(geojsonModel) geojsonString = String(data: geojsonData, encoding: .utf8) ?? ""
这种方法更类型安全,但仅适用于GeoJSON结构固定的场景。
内容的提问来源于stack exchange,提问作者Zahid
相关产品推荐
相关产品推荐

