How do I parse this nested JSON using Codable with Swift?

为君一笑 提交于 2019-12-13 09:28:02

问题


I am trying to parse this JSON using Codable:

{
  "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"
    }
]
}

I am keeping the URL private on here but it is returning JSON above. So far this is my code:

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()

I need to be able to access the User properties but I think the nesting is making it difficult for me. Any help is awesome!! Thank you!!


回答1:


First of all: Catch always the DecodingError and print it. It tells you exactly what's wrong.

The error occurs because you are ignoring the root object Users. Your code works if you decode(Users.self.

My suggestions:

  • Decode createdDate as Date adding a appropriate date decoding strategy.
  • Decode profileImage as URL (for free).
  • Handle all errors.

struct Root : Decodable { // `Users` and `User` is too confusing
    let users: [User]
}

struct User : Decodable {
    let id: Int
    let name: String
    let userName: String
    let profileImage: URL
    let createdDate: Date
}

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

    if let error = error { print(error); return }
    do {
        let decoder = JSONDecoder()
        let dateFormatter = DateFormatter()
        dateFormatter.locale = Locale(identifier: "en_US_POSIX")
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
        decoder.dateDecodingStrategy = .formatted(dateFormatter)
        let result = try decoder.decode(Root.self, from: data!)
        for user in result.users {
           print(user.userName, user.id, user.createdDate)
        }
    } catch {
        print(error)
    }

}.resume()



回答2:


Please try the below code. This is working for me.

Model Class:

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

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

Network class:

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
}



回答3:


The root of the json is a dictionary not an array you can write a root class but it will be useless , so you need

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()



回答4:


Your Users struct (I renamed it to UsersResponse) should contain a users property of type [User]. Then you can do the following:

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()


来源:https://stackoverflow.com/questions/57131821/how-do-i-parse-this-nested-json-using-codable-with-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!