Ios 如何使用带Swift的Codable解析这个嵌套JSON?

Ios 如何使用带Swift的Codable解析这个嵌套JSON?,ios,json,swift,codable,Ios,Json,Swift,Codable,我正在尝试使用Codable解析此JSON: { "users": [ { "id": 1, "name": "Allen Carslake", "userName": "acarslake0", "profileImage": "https://source.unsplash.com/random/400x400", "createdDate": "2019-07-08T00:00:00.000+0000" },

我正在尝试使用Codable解析此JSON:

{
  "users": [
    {
      "id": 1,
      "name": "Allen Carslake",
      "userName": "acarslake0",
      "profileImage": "https://source.unsplash.com/random/400x400",
      "createdDate": "2019-07-08T00:00:00.000+0000"
    },
    {
      "id": 2,
      "name": "Revkah Antuk",
      "userName": "rantuk1",
      "profileImage": "https://source.unsplash.com/random/400x400",
      "createdDate": "2019-07-05T00:00:00.000+0000"
    },
    {
      "id": 3,
      "name": "Mirna Saffrin",
      "userName": "msaffrin2",
      "profileImage": "https://source.unsplash.com/random/400x400",
      "createdDate": "2019-05-19T00:00:00.000+0000"
    },
    {
      "id": 4,
      "name": "Haily Eilers",
      "userName": "heilers3",
      "profileImage": "https://source.unsplash.com/random/400x400",
      "createdDate": "2019-06-28T00:00:00.000+0000"
    },
    {
      "id": 5,
      "name": "Oralie Polkinhorn",
      "userName": "opolkinhorn4",
      "profileImage": "https://source.unsplash.com/random/400x400",
      "createdDate": "2019-06-04T00:00:00.000+0000"
    }
]
}
我在这里保持URL的私有性,但它返回上面的JSON。到目前为止,这是我的代码:

import UIKit

struct User: Codable {
    let id: Int
    let name: String
    let userName: String
    let profileImage: String
    let createdDate: String
}

struct Users: Codable {
    let users: String
}

let url = URL(string: "")!

URLSession.shared.dataTask(with: url) { data, _, _ in

    if let data = data {

        let users = try? JSONDecoder().decode([User].self, from: data)
        print(users)
    }

    }.resume()

我需要能够访问用户属性,但我认为嵌套对我来说很困难。任何帮助都是了不起的!!谢谢

json的根是一个字典,不是一个数组,您可以编写一个根类,但它将是无用的,因此您需要

URLSession.shared.dataTask(with: url) { data, _, _ in

   do {     
    if let data = data { 
        let res = try JSONSerialization.jsonObject(with: data) as! [String:Any]
        let usersData = try JSONSerialization.data(withJSONObject: res["users"])
        let users = try JSONDecoder().decode([User].self, from: usersData) 
        print(users)
    }
   }
   catch {
     print(error)
   }

}.resume()

您的
Users
结构(我将其重命名为
UsersResponse
)应包含
Users
类型的
[User]
属性。然后您可以执行以下操作:

struct UsersResponse: Codable {
    let users: [User]
}

URLSession.shared.dataTask(with: url) { data, _, _ in
    guard let data = data else { return }

    if let users = try? JSONDecoder().decode(Users.self, from: data).users {
        users.forEach { user in
            print("A user called \(user.name) with an id of \(user.id).")
        }
    }
}.resume()

请尝试下面的代码。这对我有用

型号类别:

struct UsersResponse: Codable {
   let users: [User]
}

struct User: Codable {
    let id: Int
    let name: String
    let userName: String
    let profileImage: String?
    let createdDate: String
}
public enum EndPoints: String {
    case prod = "ProdURL"
    case test = "testURL"
}

public enum Result<T> {
    case success(T)
    case failure(Error)
}

final public class Networking: NSObject {


// MARK: - Private functions
private static func getData(url: URL,
                            completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

/// fetchUsersResponse function will fetch the User Response and returns
/// Result<UsersResponse> as completion handler

 public static func fetchUsersResponse(shouldFail: Bool = false, completion: @escaping (Result<UsersResponse>) -> Void) {
    var urlString: String?
    if shouldFail {
        urlString = EndPoints.test.rawValue
    } else {
        urlString = EndPoints.prod.rawValue
    }

    guard let mainUrlString = urlString,  let url = URL(string: mainUrlString) else { return }

    Networking.getData(url: url) { (data, response, error) in
        if let error = error {
            completion(.failure(error))
            return
        }

        guard let data = data, error == nil else { return }

        do {
            let decoder = JSONDecoder()

            //decoder.dateDecodingStrategy = .millisecondsSince1970
            decoder.dateDecodingStrategy = .formatted(setDateFormat())

            let json = try decoder.decode(UsersResponse.self, from: data)
            completion(.success(json))
        } catch let error {
            completion(.failure(error))
        }
    }
}

func setDateFormat() -> DateFormatter {
    let dateFormat = DateFormatter()
    dateFormat.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
    return dateFormat
}
网络类:

struct UsersResponse: Codable {
   let users: [User]
}

struct User: Codable {
    let id: Int
    let name: String
    let userName: String
    let profileImage: String?
    let createdDate: String
}
public enum EndPoints: String {
    case prod = "ProdURL"
    case test = "testURL"
}

public enum Result<T> {
    case success(T)
    case failure(Error)
}

final public class Networking: NSObject {


// MARK: - Private functions
private static func getData(url: URL,
                            completion: @escaping (Data?, URLResponse?, Error?) -> ()) {
    URLSession.shared.dataTask(with: url, completionHandler: completion).resume()
}

/// fetchUsersResponse function will fetch the User Response and returns
/// Result<UsersResponse> as completion handler

 public static func fetchUsersResponse(shouldFail: Bool = false, completion: @escaping (Result<UsersResponse>) -> Void) {
    var urlString: String?
    if shouldFail {
        urlString = EndPoints.test.rawValue
    } else {
        urlString = EndPoints.prod.rawValue
    }

    guard let mainUrlString = urlString,  let url = URL(string: mainUrlString) else { return }

    Networking.getData(url: url) { (data, response, error) in
        if let error = error {
            completion(.failure(error))
            return
        }

        guard let data = data, error == nil else { return }

        do {
            let decoder = JSONDecoder()

            //decoder.dateDecodingStrategy = .millisecondsSince1970
            decoder.dateDecodingStrategy = .formatted(setDateFormat())

            let json = try decoder.decode(UsersResponse.self, from: data)
            completion(.success(json))
        } catch let error {
            completion(.failure(error))
        }
    }
}

func setDateFormat() -> DateFormatter {
    let dateFormat = DateFormatter()
    dateFormat.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
    return dateFormat
}
公共枚举终结点:字符串{
case prod=“ProdURL”
case test=“testURL”
}
公共枚举结果{
成功案例(T)
案例失败(错误)
}
最终公共类网络:NSObject{
//马克:私人职能
私有静态func getData(url:url,
完成:@转义(数据?、URL响应?、错误?->()){
URLSession.shared.dataTask(带:url,completionHandler:completion).resume()
}
///fetchUsersResponse函数将获取用户响应并返回
///结果作为完成处理程序
公共静态func fetchUsersResponse(shouldFail:Bool=false,completion:@escaping(Result)->Void){
var-urlString:String?
如果失败了{
urlString=EndPoints.test.rawValue
}否则{
urlString=EndPoints.prod.rawValue
}
guard let mainUrlString=urlString,let url=url(string:mainUrlString)else{return}
Networking.getData(url:url){(数据、响应、错误)在
如果let error=error{
完成(.failure(error))
返回
}
guard let data=data,error==nil else{return}
做{
let decoder=JSONDecoder()
//decoder.dateDecodingStrategy=.毫秒ssince1970
decoder.dateDecodingStrategy=.formatted(setDateFormat())
让json=try decoder.decode(UsersResponse.self,from:data)
完成(.success(json))
}捕捉错误{
完成(.failure(error))
}
}
}
func setDateFormat()->DateFormatter{
让dateFormat=DateFormatter()
dateFormat.dateFormat=“yyyy-MM-dd'T'HH:MM:ss.SSSZ”
返回日期格式
}

首先:
捕获
始终是
解码错误
打印
它。它会准确地告诉您出了什么问题

发生此错误的原因是您忽略了根对象
Users
。如果您
解码(Users.self
,则代码有效

我的建议是:

  • createdDate
    解码为
    Date
    添加适当的日期解码策略
  • profileImage
    解码为
    URL
    (免费)
  • 处理所有错误


如果有更多属性可访问,编写
UsersResponse
将有意义,但由于它只是
users
键,因此不需要它。不过,我更喜欢这个解决方案,而不是你的
json序列化,@Sh_Khan.:)成功了,谢谢!现在我如何访问用户属性,如“id”和“profileImage”?@ReissZurbyk如果你问这样的问题,那么你需要学习数组和使用循环,我可以建议你,这是学习swift甚至以后的一个极好的资源on@JoakimDanielson.我已经添加了错误处理我的答案,请检查。你的答案看起来有点过头了,但有两件事我想知道,第一,你为什么这么做让结构中的每个属性都是可选的,只有当你知道它们是可选的时才让它们成为可选的,其次是什么是
decoder.dateDecodingStrategy=.millissecondssince1970
?感谢你的通知,@JoakimDanielson,我使用了我的一个模板代码。所以在上面的JSON结构中,没有日期格式,所以我可以删除dateDecodingSt策略。对于结构中的可选属性,我曾经按照这种做法来确保代码行为的安全性,以防服务器出现null/nil值或服务器响应中缺少任何键,那么它不会中断。如果缺少强制键,那么它应该中断,否则以后可能会出现不可预知的行为。我们都有自己的首选项我认为让一切都成为可选的是一个坏习惯。@Animesh实际上有一种日期格式,但它不是
.millissecondssince1970
。关于安全性,可选的和捕获的
解码错误之间根本没有区别。在这两种情况下,解码过程都会失败,但在非可选的情况下,您会得到一个错误全面的错误消息,您可以立即修复代码。谢谢,@vadian的输入。您能告诉我应该使用什么来代替.millissecondssince1970吗