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

如何修复JSONDecoder解码异常:预期解码Dictionary<String, Any>却得到字符串/数据?自定义Decodable枚举解码方案咨询

解决JSONDecoder解码报错及自定义Decodable枚举的问题

一、解码报错的核心原因

你碰到的Expected to decode Dictionary<String, Any> but found a string/data instead错误,本质是解码逻辑找不到合适的方式将JSON值映射到你指定的类型。结合你的代码来看,问题出在自定义的JSON枚举上——你只声明它遵循Decodable协议,但没有实现协议要求的init(from:)方法,默认解码逻辑根本不知道该怎么把JSON里的字符串、数字对应到枚举的不同case里,自然会抛出类型不匹配的报错。

二、自定义JSON枚举的问题分析

你的JSON枚举虽然定义了所有可能的JSON类型,但缺少关键的解码逻辑。Decodable的默认实现只适合结构体/类的属性一一映射,对于枚举这种需要根据值类型动态选择case的场景,必须手动实现init(from:)方法来处理每一种可能的JSON类型。

三、正确实现自定义Decodable枚举

下面是修复后的完整JSON枚举代码,补充了核心的解码逻辑:

public enum JSON: Decodable {
    case string(String)
    case number(Float)
    case object([String: JSON])
    case array([JSON])
    case bool(Bool)
    case null // 别忘了JSON里常见的null类型!

    public init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        
        // 按优先级尝试解码不同类型,注意顺序:先布尔,再字符串,数字,数组,对象,最后null
        // 这个顺序能避免布尔值被误解析为数字(比如true会被Float解码成1.0)
        if let boolValue = try? container.decode(Bool.self) {
            self = .bool(boolValue)
        } else if let stringValue = try? container.decode(String.self) {
            self = .string(stringValue)
        } else if let numberValue = try? container.decode(Float.self) {
            self = .number(numberValue)
        } else if let arrayValue = try? container.decode([JSON].self) {
            self = .array(arrayValue)
        } else if let objectValue = try? container.decode([String: JSON].self) {
            self = .object(objectValue)
        } else if container.decodeNil() {
            self = .null
        } else {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "无法解码为JSON枚举支持的类型")
        }
    }
}

关键细节说明:

  • 新增.nullcase,因为JSON中经常会出现null值,漏掉会导致解码失败
  • 使用singleValueContainer()获取当前解码的JSON值,统一处理所有类型
  • 解码顺序很重要:布尔值优先于数字,避免true/false被误解析为1.0/0.0

四、修复后的解码代码示例

现在用这个修复后的枚举来解码你的JSON数据:

do {
    let jsonData = try Data(contentsOf: urls[0])
    let decoder = JSONDecoder()
    let decodedObject = try decoder.decode([String: JSON].self, from: jsonData)
    
    // 访问解码后的值
    if case .string(let name) = decodedObject["name"] {
        print("Name: \(name)") // 输出:Name: fff
    }
    if case .number(let price) = decodedObject["price"] {
        print("Price: \(price)") // 输出:Price: 10.0
    }
} catch {
    print("解码失败:\(error)")
}

五、除JSONSerialization外的其他解码方式

除了JSONSerialization(手动处理Any类型,类型不安全),常用的解码方式还有两种:

1. 强类型Decodable模型(推荐)

如果你的JSON结构固定,最推荐这种方式——定义对应的结构体/类,直接用JSONDecoder解码,类型安全且代码简洁:

struct Product: Decodable {
    let name: String
    let price: Float
}

// 解码代码
do {
    let jsonData = try Data(contentsOf: urls[0])
    let decoder = JSONDecoder()
    let product = try decoder.decode(Product.self, from: jsonData)
    print("Name: \(product.name), Price: \(product.price)")
} catch {
    print("解码失败:\(error)")
}

2. 通用JSON枚举(你尝试的方式)

就是上面修复后的JSON枚举方式,适合JSON结构不固定、需要动态处理不同类型的场景,缺点是访问值时需要用case判断,代码稍繁琐。

内容的提问来源于stack exchange,提问作者János

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 14:02:34