【问题标题】:Nested Container nil value custom decoder Swift嵌套容器 nil 值自定义解码器 Swift
【发布时间】:2022-01-08 14:14:38
【问题描述】:

所以我有这个简单的 json 响应:

{
    "gender": "male",
    "name": {
        "title": "mr",
        "first": "brad",
        "last": "gibson"
        }
}

这是我的客户解码器:

struct UserModel: Decodable {
    var gender: String
    var title: String?
    var first: String?
    var last: String?
    
    // Top-level coding keys
    enum CodingKeys: String, CodingKey {
        case name, gender
    }
    
    enum NameKeys: CodingKey {
        case title, first, last
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        gender = try container.decode(String.self, forKey: .gender)
        let name = try container.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        title = try name.decodeIfPresent(String.self, forKey: .title)
        first = try name.decodeIfPresent(String.self, forKey: .first)
        last = try name.decodeIfPresent(String.self, forKey: .last)
    }
}

如果嵌套对象名称(title、first、last)中的一个键未发送,我知道如何处理错误情况。我的问题是,如果没有从响应中发回“名称”对象,我将如何编写一些内容来防止崩溃?

【问题讨论】:

  • 创建一个嵌套的Codable 类型。你需要哪些参数,就gender
  • 是的,例如,假设由于某种原因“姓名”没有在响应中发送,而只是返回了性别,它会崩溃。
  • 名称是否应该总是包含titlefirstlast?目前它只是将它们默认为空字符串而不是nil
  • 已更新,因此它们为零 @George
  • 顺便说一句 ?? nil 是多余的

标签: ios json swift networking decoding


【解决方案1】:

您可以嵌套Codable 结构,例如:

struct User: Codable {
    let gender: String
    let name: UserName?
}

struct UserName: Codable {
    let title: String?
    let first: String?
    let last: String?
}

为了使语法优于user.name?.title,我们可以使用@dynamicMemberLookup 将其更改为user.title。将User 更改为:

@dynamicMemberLookup
struct User: Codable {
    let gender: String
    let name: UserName?

    subscript<T>(dynamicMember k: KeyPath<UserName, T>) -> T? {
        name?[keyPath: k]
    }

    subscript<T>(dynamicMember k: KeyPath<UserName, T?>) -> T? {
        name?[keyPath: k]
    }
}

现在,您可以使用速记形式来获取每个属性。现在,这将模拟您的具有所有属性的单个容器。

【讨论】:

  • 这是一个很酷的技巧。不知道 dynamicMemberLookup 谢谢!
【解决方案2】:

下面用怎么样?

struct Name: Decodable {

    var title: String?
    var first: String?
    var last: String?

}

struct UserModel: Decodable {
    var name: Name?
    var gender: String
}

然后其他一切都会自行处理。例如,以下所有内容都将被解析而不会出现问题。

{
    "gender": "male",
    "name": {
        "title": "mr",
        "first": "brad",
        "last": "gibson"
        }
}

{
    "gender": "male"
}

{
    "gender": "male",
    "name": {
        "title": "mr",
        "first": "brad"
        }
}

我在下面测试过

let str = """
    {
        "gender": "male",
        "name": {
            "title": "mr",
            "first": "brad"
            }
    }
    """

let usermodel = try! JSONDecoder().decode(UserModel.self, from: str.data(using: .utf8)!)
print(usermodel)

【讨论】:

【解决方案3】:

感谢 Deepika,这是解决方案:

struct UserModel: Decodable {
var gender: String
var title: String?
var first: String?
var last: String?

// Top-level coding keys
enum CodingKeys: String, CodingKey {
    case name, gender
}

enum NameKeys: CodingKey {
    case title, first, last
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    gender = try container.decode(String.self, forKey: .gender)
    
    if container.contains(.name) {
        let name = try container.nestedContainer(keyedBy: NameKeys.self, forKey: .name)
        title = try name.decode(String.self, forKey: .title)
        first = try name.decode(String.self, forKey: .first)
        last = try name.decode(String.self, forKey: .last)
    }
}

}

【讨论】:

    猜你喜欢
    • 2018-06-30
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多