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

Swift中UserModel不遵循Decodable协议问题求助

问题排查与解决:UserModel 不遵循 Decodable/Equatable/Hashable 协议

核心问题拆解

1. 初始 Decodable 兼容错误原因

初始代码里,CodingKeys仅定义了tokenIssuedAt、createdAt、updatedAt三个映射字段,但结构体中还有id、email、settings三个属性未在CodingKeys中声明。编译器自动合成Codable实现时,若存在CodingKeys,只会处理其中列出的字段,导致找不到剩余属性的解码规则,触发Type 'UserModel' does not conform to protocol 'Decodable'错误。

另外,嵌套的Settings结构体存在字段名不匹配:JSON中是"wishlist",代码里写的是wishlists(多了末尾的s),这会直接导致解码失败,进一步加剧Decodable兼容问题。

2. 自定义初始化器引发的 Equatable/Hashable 错误

添加自定义init(from:)初始化器后,编译器无法自动合成Equatable和Hashable的实现——这两个协议的自动合成依赖于编译器能完整推断所有属性的比较/哈希逻辑,自定义初始化器打破了这个条件,因此出现新的协议不兼容错误。

修复方案

方案一:依赖编译器自动合成(推荐)

无需自定义初始化器,只需修正两个关键点:

  • 让CodingKeys包含所有结构体属性,需要映射的字段保留别名,其余字段直接用原名。
  • 修正Settings的字段名,与JSON完全匹配。

完整代码:

struct UserModel: Codable, Hashable, Identifiable {
    let id: String
    let email: String
    let tokenIssuedAt: Int
    let createdAt: String
    let updatedAt: String
    let settings: Settings

    enum CodingKeys: String, CodingKey {
        case id, email, settings
        case tokenIssuedAt = "token_issued_at"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }
    
    struct Settings: Codable, Hashable {
        var wishlist, collection, findable: Bool
    }
}

方案二:自定义初始化器 + 手动实现 Equatable/Hashable

如果必须使用自定义初始化器(比如有特殊解码逻辑),需要:

  1. 在初始化器中解码所有属性,不能只处理settings。
  2. 手动实现Equatable的==方法和Hashable的hash(into:)方法。

示例代码:

struct UserModel: Codable, Hashable, Identifiable {
    let id: String
    let email: String
    let tokenIssuedAt: Int
    let createdAt: String
    let updatedAt: String
    let settings: Settings

    enum CodingKeys: String, CodingKey {
        case id, email, settings
        case tokenIssuedAt = "token_issued_at"
        case createdAt = "created_at"
        case updatedAt = "updated_at"
    }
    
    // 自定义解码初始化器,必须处理所有属性
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        id = try container.decode(String.self, forKey: .id)
        email = try container.decode(String.self, forKey: .email)
        tokenIssuedAt = try container.decode(Int.self, forKey: .tokenIssuedAt)
        createdAt = try container.decode(String.self, forKey: .createdAt)
        updatedAt = try container.decode(String.self, forKey: .updatedAt)
        settings = try container.decode(Settings.self, forKey: .settings)
    }
    
    struct Settings: Codable, Hashable {
        var wishlist, collection, findable: Bool
    }
    
    // 手动实现Equatable
    static func == (lhs: UserModel, rhs: UserModel) -> Bool {
        return lhs.id == rhs.id &&
               lhs.email == rhs.email &&
               lhs.tokenIssuedAt == rhs.tokenIssuedAt &&
               lhs.createdAt == rhs.createdAt &&
               lhs.updatedAt == rhs.updatedAt &&
               lhs.settings == rhs.settings
    }
    
    // 手动实现Hashable
    func hash(into hasher: inout Hasher) {
        hasher.combine(id)
        hasher.combine(email)
        hasher.combine(tokenIssuedAt)
        hasher.combine(createdAt)
        hasher.combine(updatedAt)
        hasher.combine(settings)
    }
}

验证解码

可用以下代码测试解码是否正常:

let jsonString = """
[
    {
        "id": "1cc02c5f-2cb3-4546-a2d7-a824188e667d",
        "email": "trenton@example.com",
        "token_issued_at": 1676418140,
        "created_at": "2023-02-09T01:51:08.733Z",
        "updated_at": "2023-02-14T23:46:14.136Z",
        "settings": {
            "collection": false,
            "wishlist": false,
            "findable": true
        }
    }
]
"""

if let jsonData = jsonString.data(using: .utf8) {
    do {
        let users = try JSONDecoder().decode([UserModel].self, from: jsonData)
        print(users.first?.settings.wishlist) // 输出 false,符合JSON数据
    } catch {
        print("解码失败:\(error)")
    }
}

内容的提问来源于stack exchange,提问作者Anon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 20:15:50