Swift 中的 JSON 解码。无法读取数据,因为它的格式不正确

Posted

技术标签:

【中文标题】Swift 中的 JSON 解码。无法读取数据,因为它的格式不正确【英文标题】:JSON decoding in Swift. The data couldn’t be read because it isn’t in the correct format 【发布时间】: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 那确实是无效的。 【参考方案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 你能检查一下编辑后的答案吗?

以上是关于Swift 中的 JSON 解码。无法读取数据,因为它的格式不正确的主要内容,如果未能解决你的问题,请参考以下文章

我如何使用解码器在swift 4中解析tableview中的json数组

由于字符周围的值无效,无法解码 json

将 JSON 数据从 Parse Cloud Code 返回到 Swift 中的可解码结构

无法在 Swift 中解析 JSON。 “应解码 Dictionary<String, Any>,但找到了一个数组。” [复制]

无法在单元测试项目中的 Swift 中读取 JSON 文件

Swift - JSON解码返回空数组