如何修复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
相关产品推荐
相关产品推荐

