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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 12:07:40