Swift中如何仅当[String: Any]字典非nil时执行编码?
实现[String: Any]字典的条件编码(类似encodeIfPresent)
当然可以实现这种仅当可选字典非nil时才编码的逻辑,核心是处理[String: Any]不遵循Codable的问题,同时手动判断字典是否存在再执行编码。
步骤1:实现通用的AnyCodable封装
因为原生Swift没有直接支持Any类型的Codable实现,我们需要自己写一个简单的封装结构体,让任意基础类型(字符串、数字、布尔值、数组、字典)都能被编码:
struct AnyCodable: Codable { let value: Any init(_ value: Any) { self.value = value } func encode(to encoder: Encoder) throws { var container = encoder.singleValueContainer() switch value { case let num as Int: try container.encode(num) case let num as Double: try container.encode(num) case let str as String: try container.encode(str) case let bool as Bool: try container.encode(bool) case let array as [Any]: try container.encode(array.map(AnyCodable.init)) case let dict as [String: Any]: try container.encode(dict.mapValues(AnyCodable.init)) default: throw EncodingError.invalidValue( value, EncodingError.Context( codingPath: encoder.codingPath, debugDescription: "不支持的类型:\(type(of: value))" ) ) } } // 如果不需要解码功能,可以简化这个方法,这里仅做完整实现 init(from decoder: Decoder) throws { let container = try decoder.singleValueContainer() if let int = try? container.decode(Int.self) { value = int } else if let double = try? container.decode(Double.self) { value = double } else if let string = try? container.decode(String.self) { value = string } else if let bool = try? container.decode(Bool.self) { value = bool } else if let array = try? container.decode([AnyCodable].self) { value = array.map(\.value) } else if let dict = try? container.decode([String: AnyCodable].self) { value = dict.mapValues(\.value) } else { throw DecodingError.dataCorrupted( DecodingError.Context( codingPath: decoder.codingPath, debugDescription: "无法解码的类型" ) ) } } }
步骤2:在自定义模型中实现条件编码
定义包含可选[String: Any]字典的模型,并重写encode(to:)方法,仅当字典非nil时才编码对应的字段:
struct UserInfo: Codable { var username: String var extraInfo: [String: Any]? // 可选的[String: Any]字典 enum CodingKeys: String, CodingKey { case username case extraInfo } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) // 必选字段直接编码 try container.encode(username, forKey: .username) // 仅当extraInfo非nil时才编码,模拟encodeIfPresent的行为 if let info = extraInfo { let codableInfo = info.mapValues(AnyCodable.init) try container.encode(codableInfo, forKey: .extraInfo) } } }
步骤3:测试效果
// 测试包含字典的情况 let userWithExtra = UserInfo( username: "Alice", extraInfo: ["age": 28, "isVerified": true, "hobbies": ["reading", "hiking"]] ) let encoder = JSONEncoder() encoder.outputFormatting = .prettyPrinted if let data = try? encoder.encode(userWithExtra), let jsonStr = String(data: data, encoding: .utf8) { print("包含字典的编码结果:\n\(jsonStr)") } // 测试字典为nil的情况 let userWithoutExtra = UserInfo(username: "Bob", extraInfo: nil) if let data = try? encoder.encode(userWithoutExtra), let jsonStr = String(data: data, encoding: .utf8) { print("\n字典为nil的编码结果:\n\(jsonStr)") }
输出结果
- 包含字典时,JSON会有
extraInfo字段:
{ "username" : "Alice", "extraInfo" : { "age" : 28, "isVerified" : true, "hobbies" : [ "reading", "hiking" ] } }
- 字典为nil时,JSON不会包含
extraInfo字段:
{ "username" : "Bob" }
内容的提问来源于stack exchange,提问作者Gagan Chordia
相关产品推荐
相关产品推荐

