Ios Swift JSON解码器';在Flickr的JSON数据中找不到密钥

Ios Swift JSON解码器';在Flickr的JSON数据中找不到密钥,ios,json,swift,Ios,Json,Swift,我在一本书的一个例子中遇到了一个json解码错误。错误显示: 2021-05-06 07:01:31.193094+1000 Photorama[1562:29734] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed Error fetching interesting photos: keyNotFound(CodingKeys(stringValue: "photos", intVa

我在一本书的一个例子中遇到了一个json解码错误。错误显示:

    2021-05-06 07:01:31.193094+1000 Photorama[1562:29734] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed
Error fetching interesting photos: keyNotFound(CodingKeys(stringValue: "photos", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "photos", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"photos\", intValue: nil) (\"photos\").", underlyingError: nil))
然而,在Flickr返回的json数据中,键“photos”确实存在。json数据的缩写副本如下所示:

    {
  "extra": {
    "explore_date": "2021-05-04",
    "next_prelude_interval": 57778
  },
  "photos": {
    "page": 1,
    "pages": 5,
    "perpage": 100,
    "total": 500,
    "photo": [
      {
        "id": "51156301899",
        "owner": "138752302@N05",
        "secret": "31d327f54f",
        "server": "65535",
        "farm": 66,
        "title": "*the power of the sun*",
        "ispublic": 1,
        "isfriend": 0,
        "isfamily": 0,
        "datetaken": "2021-04-20 06:52:11",
        "datetakengranularity": "0",
        "datetakenunknown": "0",
        "url_z": "https://live.staticflickr.com/65535/51156301899_31d327f54f_z.jpg",
        "height_z": 380,
        "width_z": 640
      },
      "stat": "ok"
      }
程序代码如下: AppDelegate.swift(无更改)

斯威夫特

import UIKit

class PhotosViewController: UIViewController {
    
    @IBOutlet private var imageView: UIImageView!
    var store: PhotoStore!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        // store.fetchInterestingPhotos()
        store.fetchInterestingPhotos {
            (photoResult) in
            
            switch photoResult {
            case let .success(photos):
                print("Successfully found \(photos.count) photos")
            case let .failure(error):
                print("Error fetching interesting photos: \(error)")
            }
        }
        
    }


}
斯威夫特

import Foundation

enum EndPoint: String {
    case interestingPhotos = "flickr.interestingness.getList"
}
/*
 https://api.flickr.com/services/rest/?method=flickr.interestingness.getList
 &api_key=a6d819499131071f158fd740860a5a88&extras=url_z,date_taken
 &format=json&nojsoncallback=1
 */
struct FlickrAPI {
    private static let baseURLString = "https://api.flickr.com/services/rest"
    private static let apiKey = "a6d819499131071f158fd740860a5a88"
    
    // assemble url from baseURLString and query items
    private static func flickrURL (endPoint: EndPoint, parameters: [String: String]?) -> URL {
        var components = URLComponents(string: baseURLString)!
        var queryItems = [URLQueryItem]()
        let baseParams = [
            "method": endPoint.rawValue,
            "format": "json",
            "nojsoncallback": "1",
            "api_key": apiKey
        ]
        for (key, value) in baseParams {
            let item = URLQueryItem(name: key, value: value)
            queryItems.append(item)
        }
        if let addtionalParams = parameters {
            for (key, value) in addtionalParams {
                let item = URLQueryItem(name: key, value: value)
                queryItems.append(item)
            }
        }
        components.queryItems = queryItems
        
        return components.url!
    }
    static var interestingPhotoURL: URL {
        // url_z is a URL shortener (zipper) for convenience and beauty
        return flickrURL(endPoint: .interestingPhotos,
                         parameters: ["extras": "url_z,date_taken"])
    }
    static func photos (fromJSON data: Data) -> Result<[Photo], Error> {
        do {
            let decoder = JSONDecoder()
            let flickrResponse = try decoder.decode(FlickrResponse.self, from: data)
    
            return .success(flickrResponse.photosInfo.photos)
        } catch {
            return .failure(error)
        }
    }
}

struct FlickrResponse: Codable {
    //let photos: FlickrPhotosResponse
    let photosInfo: FlickrPhotosResponse
    
    enum CodingKeys: String, CodingKey {
        case photosInfo = "photos"
    }
}

struct FlickrPhotosResponse: Codable {
    //let photo: [Photo]
    let photos: [Photo]
    
    enum CodingKyes: String, CodingKey {
        case photos = "photo"
    }
}

我想知道问题在哪里以及如何解决。非常感谢你的帮助

json中的
照片
不是数组。它是一个物体

将json粘贴到中以获得正确的可编码对象

// MARK: - FlickrPhotosResponse
struct FlickrPhotosResponse: Codable {
    let extra: Extra
    let photos: Photos
}

// MARK: - Extra
struct Extra: Codable {
    let exploreDate: String
    let nextPreludeInterval: Int

    enum CodingKeys: String, CodingKey {
        case exploreDate = "explore_date"
        case nextPreludeInterval = "next_prelude_interval"
    }
}

// MARK: - Photos
struct Photos: Codable {
    let page, pages, perpage, total: Int
    let photo: [Photo]
    let stat: String
}

// MARK: - Photo
struct Photo: Codable {
    let id, owner, secret, server: String
    let farm: Int
    let title: String
    let ispublic, isfriend, isfamily: Int
    let datetaken, datetakengranularity, datetakenunknown: String
    let urlZ: String
    let heightZ, widthZ: Int

    enum CodingKeys: String, CodingKey {
        case id, owner, secret, server, farm, title, ispublic, isfriend, isfamily, datetaken, datetakengranularity, datetakenunknown
        case urlZ = "url_z"
        case heightZ = "height_z"
        case widthZ = "width_z"
    }
}

尝试将您的API响应粘贴到app.quicktype.io中,并使用生成的代码—您在问题中提供的JSON格式无效。此外,请仅提供需要其他人检查的代码。不需要包括appdelegate、scenedelegate等。如果不需要,您需要学习阅读和解释错误消息,大多数情况下这并不难,对于软件开发人员来说,这是必要的知识
import Foundation

enum EndPoint: String {
    case interestingPhotos = "flickr.interestingness.getList"
}
/*
 https://api.flickr.com/services/rest/?method=flickr.interestingness.getList
 &api_key=a6d819499131071f158fd740860a5a88&extras=url_z,date_taken
 &format=json&nojsoncallback=1
 */
struct FlickrAPI {
    private static let baseURLString = "https://api.flickr.com/services/rest"
    private static let apiKey = "a6d819499131071f158fd740860a5a88"
    
    // assemble url from baseURLString and query items
    private static func flickrURL (endPoint: EndPoint, parameters: [String: String]?) -> URL {
        var components = URLComponents(string: baseURLString)!
        var queryItems = [URLQueryItem]()
        let baseParams = [
            "method": endPoint.rawValue,
            "format": "json",
            "nojsoncallback": "1",
            "api_key": apiKey
        ]
        for (key, value) in baseParams {
            let item = URLQueryItem(name: key, value: value)
            queryItems.append(item)
        }
        if let addtionalParams = parameters {
            for (key, value) in addtionalParams {
                let item = URLQueryItem(name: key, value: value)
                queryItems.append(item)
            }
        }
        components.queryItems = queryItems
        
        return components.url!
    }
    static var interestingPhotoURL: URL {
        // url_z is a URL shortener (zipper) for convenience and beauty
        return flickrURL(endPoint: .interestingPhotos,
                         parameters: ["extras": "url_z,date_taken"])
    }
    static func photos (fromJSON data: Data) -> Result<[Photo], Error> {
        do {
            let decoder = JSONDecoder()
            let flickrResponse = try decoder.decode(FlickrResponse.self, from: data)
    
            return .success(flickrResponse.photosInfo.photos)
        } catch {
            return .failure(error)
        }
    }
}

struct FlickrResponse: Codable {
    //let photos: FlickrPhotosResponse
    let photosInfo: FlickrPhotosResponse
    
    enum CodingKeys: String, CodingKey {
        case photosInfo = "photos"
    }
}

struct FlickrPhotosResponse: Codable {
    //let photo: [Photo]
    let photos: [Photo]
    
    enum CodingKyes: String, CodingKey {
        case photos = "photo"
    }
}
import Foundation

class PhotoStore {
    private let session: URLSession = {
        let config = URLSessionConfiguration.default
        return URLSession(configuration: config)
    }()
    
    private func processPhotosRequest (data: Data?, error: Error?) ->
    Result<[Photo], Error> {
        guard let jsonData = data else {
            return .failure(error!)
        }
        return FlickrAPI.photos(fromJSON: jsonData)
    }
    
    func fetchInterestingPhotos (completion: @escaping (Result<[Photo], Error>) -> Void) {
        let url = FlickrAPI.interestingPhotoURL
        let request = URLRequest(url: url)
        let task = session.dataTask(with: request) {
            (data, response, error) in
            
        /*if let jsonData = data {
            if let jsonString = String(data: jsonData, encoding: .utf8) {
                print(jsonString)
            }
            } else if let requestError = error {
                print("Error fetching interest photos: \(requestError)")
            } else {
                print("Unexpect error with the request")
            }*/
            let result = self.processPhotosRequest(data: data, error: error)
            completion(result)
        }
        task.resume()
    }
}
import Foundation

class Photo: Codable {
    let title: String
    let remoteURL: URL
    let photoID: String
    let dateTaken: Date
    
    public init (title: String, remoteURL: URL, photoID: String, dateTaken: Date) {
        self.title = title
        self.remoteURL = remoteURL
        self.photoID = photoID
        self.dateTaken = dateTaken
    }
    
    enum CodingKeys: String, CodingKey {
        case title
        case remoteURL = "url_z"
        case photoID = "id"
        case dateTaken = "datetaken"
    }
}
// MARK: - FlickrPhotosResponse
struct FlickrPhotosResponse: Codable {
    let extra: Extra
    let photos: Photos
}

// MARK: - Extra
struct Extra: Codable {
    let exploreDate: String
    let nextPreludeInterval: Int

    enum CodingKeys: String, CodingKey {
        case exploreDate = "explore_date"
        case nextPreludeInterval = "next_prelude_interval"
    }
}

// MARK: - Photos
struct Photos: Codable {
    let page, pages, perpage, total: Int
    let photo: [Photo]
    let stat: String
}

// MARK: - Photo
struct Photo: Codable {
    let id, owner, secret, server: String
    let farm: Int
    let title: String
    let ispublic, isfriend, isfamily: Int
    let datetaken, datetakengranularity, datetakenunknown: String
    let urlZ: String
    let heightZ, widthZ: Int

    enum CodingKeys: String, CodingKey {
        case id, owner, secret, server, farm, title, ispublic, isfriend, isfamily, datetaken, datetakengranularity, datetakenunknown
        case urlZ = "url_z"
        case heightZ = "height_z"
        case widthZ = "width_z"
    }
}