【问题标题】:JSON decoding in Swift. The data couldn’t be read because it isn’t in the correct formatSwift 中的 JSON 解码。无法读取数据,因为它的格式不正确
【发布时间】:2020-10-09 00:54:52
【问题描述】:

我在解码 JSON 时遇到问题。它说:“无法读取数据,因为它的格式不正确。” 我不能指望它有什么问题。 你介意选一个吗?

端点:https://images-api.nasa.gov/search?q=apollo%2011&media_type=video

JSON 示例:

{
"collection": {
    "version": "1.0",
    "href": "https://images-api.nasa.gov/search?q=apollo%2011&media_type=video",
    "items": [
        {
            "href": "https://images-assets.nasa.gov/video/Apollo 11 Overview/collection.json",
            "links": [
                {
                    "href": "https://images-assets.nasa.gov/video/Apollo 11 Overview/Apollo 11 Overview~thumb.jpg",
                    "render": "image",
                    "rel": "preview"
                },
                {
                    "href": "https://images-assets.nasa.gov/video/Apollo 11 Overview/Apollo 11 Overview.srt",
                    "rel": "captions"
                }
            ],
            "data": [
                {
                    "description": "Video highlights from the historic first manned landing on the moon, during the Apollo 11 mission in July 1969.",
                    "date_created": "2013-05-15T00:00:00Z",
                    "media_type": "video",
                    "keywords": [
                        "Apollo 11",
                        "Moon"
                    ],
                    "nasa_id": "Apollo 11 Overview",
                    "center": "HQ",
                    "title": "Apollo 11 Overview"
                }
            ]
        },

我的模特:

struct NasaCollection: Codable {
    var collection: Collection
}

// MARK: - Collection
struct Collection: Codable {
    let version: String
    let href: String
    let items: [Item]
}

// MARK: - Item
struct Item: Codable {
    let href: String
    let links: [ItemLink]
    let data: Datum
    
}

// MARK: - ItemLink
struct ItemLink: Codable {
    let href: String
    let render: Render?
    
    
}

// MARK: - Datum
struct Datum: Codable {
    let datumDescription: String
    let dateCreated: Date
    let keywords: [String]
    let nasaID: String
    let title: String
    let location, description508, photographer, secondaryCreator: String?
    let album: [String]?

}

enum Render: String, Codable {
    case image = "image"
}

// MARK: - CollectionLink
struct CollectionLink: Codable {
    let prompt, rel: String
    let href: String
}

// MARK: - Metadata
struct Metadata: Codable {
    let totalHits: Int

    enum CodingKeys: String, CodingKey {
        case totalHits = "total_hits"
    }
}

决定:

func getVideos(completed: @escaping (Result<[Item], Error>)-> Void) {
        let endpoint = "https://images-api.nasa.gov/search?q=apollo%2011&media_type=video"
        
        guard let url = URL(string: endpoint) else { return }
        
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            if let _ = error {
                completed(.failure(error?.localizedDescription as! Error))
            }
            
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                completed(.failure(error?.localizedDescription as! Error))
                return
            }
            
            guard let data = data else {
                completed(.failure(error?.localizedDescription as! Error))
                return
            }
            
            do {
                let decoder = JSONDecoder()
                decoder.dataDecodingStrategy = .base64
                let videos = try decoder.decode([Item].self, from: data)
                completed(.success(videos))

            } catch {
                print(error.localizedDescription)

            }
        
        }
        task.resume()
        
    }
    

我认为我的模型是正确的,它“应该”被解码。我试图解码 JSON 的根,但仍然得到同样的错误。

【问题讨论】:

  • 不要print(error.localizedDescription)。只需print(error)。您会收到更好的错误消息。
  • 如果那真的是你的 JSON 那确实是无效的。

标签: json swift codable


【解决方案1】:

首先您需要将data 类型从Datum 更改为数组[Datum],在Item 中,正如@emrcftci 在他的回答中提到的那样:

struct Item: Codable {
    let href: String
    let links: [ItemLink]
    let data: [Datum]
}

然后你需要将nasaID 属性更改为nasaIddateCreated 类型从DateString,在Datum

struct Datum: Codable {
    let description: String
    let dateCreated: String
    let keywords: [String]
    let nasaId: String
    let title: String
    let location, description508, photographer, secondaryCreator: String?
    let album: [String]?
}

最后,当你解码时,将.convertFromSnakeCase 传递给JSONDecoderkeyDecodingStrategy 属性,并在调用decode(_:from:) 函数时使用NasaCollection.self 作为类型,而不是[Item].self

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
do {
    let decoded = try decoder.decode(NasaCollection.self, from: data)
    print(decoded)
} catch {
    print(error)
}

【讨论】:

    【解决方案2】:

    Itemdata 应该是Datum 的数组 -> [Datum]

    如你所见 ->

    {
    "collection": {
        "version": "1.0",
        "href": "https://images-api.nasa.gov/search?q=apollo%2011&media_type=video",
        "items": [
            {
                "href": "https://images-assets.nasa.gov/video/Apollo 11 Overview/collection.json",
                "links": [
                    {
                        "href": "https://images-assets.nasa.gov/video/Apollo 11 Overview/Apollo 11 Overview~thumb.jpg",
                        "render": "image",
                        "rel": "preview"
                    },
                    {
                        "href": "https://images-assets.nasa.gov/video/Apollo 11 Overview/Apollo 11 Overview.srt",
                        "rel": "captions"
                    }
                ],
                "data": [ // <------ Data has an array of an object(Datum).
                    {
                        "description": "Video highlights from the historic first manned landing on the moon, during the Apollo 11 mission in July 1969.",
                        "date_created": "2013-05-15T00:00:00Z",
                        "media_type": "video",
                        "keywords": [
                            "Apollo 11",
                            "Moon"
                        ],
                        "nasa_id": "Apollo 11 Overview",
                        "center": "HQ",
                        "title": "Apollo 11 Overview"
                    }
                ]
            },
    

    您应该将data 设置为数组[Datum]

    struct Item: Codable {
        let href: String
        let links: [ItemLink]
        let data: [Datum]
    }
    

    你应该更新Datum对象如下

    {
        "description": "Video highlights from the historic first manned landing on the moon, during the Apollo 11 mission in July 1969.",
        "date_created": "2013-05-15T00:00:00Z",
        "media_type": "video",
        "keywords": [
            "Apollo 11",
            "Moon"
        ],
        "nasa_id": "Apollo 11 Overview",
        "center": "HQ",
        "title": "Apollo 11 Overview"
    }
    
    struct Datum: Codable {
        let description: String
        let dateCreated: Date
        let keywords: [String]
        let nasaID: String
        let title: String
        let location, description508, photographer, secondaryCreator: String?
        let album: [String]?
    
        enum CodingKeys: String, CodingKey {
            case dateCreated = "date_created"
            case nasaID = "nasa_id"
        }
    }
    

    奖金

    您正在尝试解析 NasaCollection 而不是 [Item]。因为在 getVideos(completed:) 函数中,你的 do 块应该是这样的 ->

    do {
        let decoder = JSONDecoder()
        decoder.dataDecodingStrategy = .base64
    
        // You should try to decode `NasaCollection`!!!
        let videos = try decoder.decode(NasaCollection.self, from: data)
        completed(.success(videos))
    }
    

    【讨论】:

    • 它说:“Swift.DecodingError.Context(codingPath: [], debugDescription: “期望解码 Array 但找到了一个字典。”,underlyingError: nil)) “它仍然没有'不解决问题...
    • @EugeneBerezin 你能检查一下编辑后的答案吗?
    猜你喜欢
    • 1970-01-01
    • 2020-03-11
    • 2021-01-15
    • 2019-07-28
    • 2021-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-12
    相关资源
    最近更新 更多