Reputation: 49
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.
Upvotes: 0
Views: 293
Reputation: 8327
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)
}
Upvotes: 1
Reputation: 3516
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))
}
Upvotes: 0