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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 23:34:58