Jos*_*den 32 json deserialization swift swift4 codable
我正在使用Codable带有JSON数据的Swift 4 协议.我的数据被格式化为在根级别有一个键,其中一个对象值包含我需要的属性,例如:
{
  "user": {
    "id": 1,
    "username": "jdoe"
  }
}
我有一个User可以解码user密钥的结构:
struct User: Codable {
  let id: Int
  let username: String
}
由于id和username是的性质user,而不是在根级别,我需要使一个包装类型,如下所示:
struct UserWrapper: Codable {
  let user: User
}
然后,我可以通过UserWrapper,解码JSON ,并User解码.它似乎是很多冗余代码,因为我需要在我拥有的每种类型上都有一个额外的包装器.有没有办法避免这种包装模式或更正确/优雅的方式来处理这种情况?
Oll*_*lie 40
您可以使用字典进行解码:用户组合然后提取出用户对象.例如
struct User: Codable {
    let id: Int
    let username: String
}
let decoder = JSONDecoder()
let userDictionary = try decoder.decode([String: User].self, from: jsonData)
Rob*_*ier 40
Ollie的回答肯定是这种情况的最佳方式,但它确实将一些知识推入调用者,这可能是不可取的.它也不是很灵活.我仍然认为这是一个很好的答案,正是你想要的,但这是探索自定义结构编码的一个很好的简单例子.
我们怎样才能正常工作:
let user = try? JSONDecoder().decode(User.self, from: json)
我们不能再使用默认的一致性了.我们必须建立自己的解码器.这有点单调乏味,但并不困难.首先,我们需要将结构编码为CodingKeys:
struct User {
    let id: Int
    let username: String
    enum CodingKeys: String, CodingKey {
        case user // The top level "user" key
    }
    // The keys inside of the "user" object
    enum UserKeys: String, CodingKey {
        case id
        case username
    }
}
有了它,我们可以User通过拉出嵌套容器手动解码:
extension User: Decodable {
    init(from decoder: Decoder) throws {
        // Extract the top-level values ("user")
        let values = try decoder.container(keyedBy: CodingKeys.self)
        // Extract the user object as a nested container
        let user = try values.nestedContainer(keyedBy: UserKeys.self, forKey: .user)
        // Extract each property from the nested container
        id = try user.decode(Int.self, forKey: .id)
        username = try user.decode(String.self, forKey: .username)
    }
}
但我绝对会为这个问题做奥利的方式.
有关此内容的更多信息,请参阅编码和解码自定义类型.
Pau*_*tos 11
当然,你总是可以实现自己的自定义解码/编码 - 但对于这个简单的场景,你的包装类型是一个更好的解决方案IMO;)
为了比较,自定义解码将如下所示:
struct User {
    var id: Int
    var username: String
    enum CodingKeys: String, CodingKey {
        case user
    }
    enum UserKeys: String, CodingKey {
        case id, username
    }
}
extension User: Decodable {
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        let user = try values.nestedContainer(keyedBy: UserKeys.self, forKey: .user)
        self.id = try user.decode(Int.self, forKey: .id)
        self.username = try user.decode(String.self, forKey: .username)
    }
}
Encodable如果你想支持编码,你仍然要遵守协议.正如我之前所说,你的简单UserWrapper要容易得多;)
我为Codable创建了一个帮助扩展,可以使这样的事情变得更容易.
请参阅https://github.com/evermeer/Stuff#codable
有了它,您可以创建一个用户对象的实例,如下所示:
    let v = User(json: json, keyPath: "user")
您不必更改原始User结构中的任何内容,也不需要包装器.
| 归档时间: | 
 | 
| 查看次数: | 13364 次 | 
| 最近记录: |