简体   繁体   English

SwiftUI macOS JSON 转换为 class Codable

[英]SwiftUI macOS JSON convert in class Codable

I get the following json from an api call but am having trouble making the proper structure in swift and then getting the data as an array.我从 api 调用中得到以下 json,但在 swift 中创建正确的结构然后将数据作为数组获取时遇到了问题。

JSON: JSON:

{
   "status":"ok",
   "users":[
      {
         "position":0,
         "user":{
            "pk":"",
            "full_name":"",
            "username":"",
            "profile_pic_url":""
         }
      },...
   ]
}

Swift: Swift:

class Response: Codable {
    var status: String
    var users: [User]?
}

class User: Identifiable, Codable {
    var uuid = UUID()
    
    var pk: String
    var full_name: String
    var username: String
    var profile_pic_url: String
    
    enum CodingKeys: String, CodingKey {
        case
        pk = "user.pk",
        full_name = "user.full_name",
        username = "user.username",
        profile_pic_url = "user.profile_pic_url"
    }
}

class Fetch: ObservableObject {
    @Published var results = [User]()
    @Published var resultState = false
    @Published var errorState = false
    init(url: String) {
        self.results = []
        let url = URL(string: url)!
        URLSession.shared.dataTask(with: url) { data, response, error in
            do {
                if let data = data {
                    let results = try JSONDecoder().decode(Response.self, from: data)
                    DispatchQueue.main.async {
                        self.results = results.users ?? []
                        self.resultState = true
                    }
                    print("Widget: Ok.")
                } else {
                    self.results = []
                    self.resultState = true
                    print("Widget: No data.")
                }
            } catch {
                self.errorState = true
                self.resultState = true
                print("Widget: Error", error)
            }
        }.resume()
    }
}

Code:代码:

@ObservedObject var fetch = Fetch(url: "")

List(fetch.results) { user in
    UserItem(user: user)
}

The problem is that inside array users, it contains an object, this object contains two elements a position attribute and then the user object.问题是在数组用户内部,它包含一个 object,这个 object 包含两个元素 position 属性,然后是用户 object。

What I think I'm doing wrong is taking the user object.我认为我做错的是占用了用户 object。

Can anyone help me out?谁能帮我吗?

Edit:编辑:

struct Response: Codable {
    let status: String
    let users: [UserType]?
}

struct UserType: Codable {
    let position: Int
    let user: User
}

struct User: Codable {
    let pk: String
    let full_name: String
    let username: String
    let profile_pic_url: String
    enum CodingKeys: String, CodingKey {
        case pk, full_name, username, profile_pic_url
    }
}

class Fetch: ObservableObject {
    @Published var results = [User]()
    @Published var resultState = false
    @Published var errorState = false
    init(url: String) {
        self.results = []
        let url = URL(string: url)!
        URLSession.shared.dataTask(with: url) { data, response, error in
            do {
                if let data = data {
                    let results = try JSONDecoder().decode(Response.self, from: data)
                    let users = results.users?.map { $0.user }
                    DispatchQueue.main.async {
                        self.results = users ?? []
                        self.resultState = true
                    }
                    print("Widget: Ok.")
                } else {
                    self.results = []
                    self.resultState = true
                    print("Widget: No data.")
                }
            } catch {
                self.errorState = true
                self.resultState = true
                print("Widget: Error", error)
            }
        }.resume()
    }
}

        List(fetch.results) { user in
            UserItem(user: user)
        }

You need an extra struct that holds the User type您需要一个额外的结构来保存 User 类型

struct UserType: Codable {
    let position: Int
    let user: User
}

Meaning the top type becomes意味着顶级类型变为

struct Response: Codable {
    let status: String
    let users: [UserType]?
}

You also need to change the CodingKeys enum since it should just contain the property names which mean the enum can be written as您还需要更改CodingKeys枚举,因为它应该只包含属性名称,这意味着枚举可以写为

enum CodingKeys: String, CodingKey {
    case pk, full_name, username, profile_pic_url
}

For completeness here is the full User type为了完整起见,这里是完整的User类型

struct User: Identifiable, Codable {
    var uuid = UUID()

    var pk: String
    var full_name: String
    var username: String
    var profile_pic_url: String

    enum CodingKeys: String, CodingKey {
        case pk, full_name, username, profile_pic_url
    }
}

and when decoding then you can extract the users array with the map function在解码时,您可以使用map function 提取用户数组

do {
    let results = try JSONDecoder().decode(Response.self, from: data)
    let users = results.users?.map { $0.user }
    ....

Note that I changed from class to struct because struct is better suited for this but class works as well.请注意,我从 class 更改为 struct,因为 struct 更适合此用途,但 class 也适用。 I also wonder why the users property is optional, I didn't change that but can the array really be nil?我也想知道为什么users属性是可选的,我没有改变它但是数组真的可以为零吗?

You can try this.你可以试试这个。

    struct Response: Codable {
    let status: String 
    let users: [UserWPosition]
    
    var userNoPositions: [UserInfo] { // computed value with only array of userinfo
        users.compactMap { $0.user }
    }
}

// MARK: - User with position object
struct UserWPosition: Codable {
    let position: Int  // commenting this will also do no effect
    let user: UserInfo
}

// MARK: - UserInfo
struct UserInfo: Codable {
    let pk, fullName, username, profilePicURL: String
    enum CodingKeys: String, CodingKey {
        case pk
        case fullName = "full_name"
        case username
        case profilePicURL = "profile_pic_url"
    }
}

Read the comments I added to the code decoding will not code a key that's not added to the struct so commenting out position will have no issue also, the hierarchy of it should be like this now I added a userNoPositions computed value in response to give array of users easily.阅读我添加到代码中的注释解码不会对未添加到struct中的键进行编码,因此注释掉position也没有问题,它的层次结构现在应该是这样的我添加了一个userNoPositions计算值以响应给数组用户轻松。
Simply to access the array without positions简单地访问没有位置的数组

var resp =  try! JSONDecoder().decode(Response.self, from: encoded)  // encoded is the data from json   
print(resp.userNoPositions) // the array 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM