0

I am having trouble decoding my JSON. It says: "The data couldn’t be read because it isn’t in the correct format." I can't put my finger on it what's wrong. Would you mind to take a pick?

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

JSON Sample:

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

My model:

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

Decding:

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

I think my model is correct and it "should" be decoded. I tried to decode the very root of JSON and still getting the same error.

2
  • 3
    Don't print(error.localizedDescription). Just print(error). You get a way better error message. Commented Oct 9, 2020 at 0:58
  • 2
    If that is really your JSON it is indeed invalid. Commented Oct 9, 2020 at 1:19

2 Answers 2

1

First you need to change data type from Datum to array [Datum], in Item, as @emrcftci mentioned in his answer:

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

Then you need to change nasaID property to nasaId and dateCreated type from Date to String, in 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]? } 

And finally when you decoding, pass .convertFromSnakeCase to keyDecodingStrategy property of JSONDecoder and use NasaCollection.self as type when calling decode(_:from:) function, instead of [Item].self:

let decoder = JSONDecoder() decoder.keyDecodingStrategy = .convertFromSnakeCase do { let decoded = try decoder.decode(NasaCollection.self, from: data) print(decoded) } catch { print(error) } 
Sign up to request clarification or add additional context in comments.

Comments

0

Item's data should be an array of Datum -> [Datum]

As you can see the response ->

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

You should set data as an array [Datum]

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

And you should update Datum object as following

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

BONUS

You are trying to parse NasaCollection instead of [Item]. Because of that in getVideos(completed:) function your do block should be like this ->

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

2 Comments

It says : "Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array<Any> but found a dictionary instead.", underlyingError: nil)) " It still didn't fix the problem...
@EugeneBerezin could you please check the edited answer?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.