Swift - Json Error: the Data Couldn'T Be Read Because It Isn'T in the Correct Format

Swift - The data couldn’t be read because it isn’t in the correct format

Try this

let gitData = try decoder.decode(Root.self, from: data!)

Traverse through your data

for singleData in gitData.data where (singleData.name ?? "") == "Cafe" {
print(singleData.image)
}

Swift : The data couldn’t be read because it isn’t in the correct format

Just your Album model is incorrect.

struct Album: Codable {
var source : Source
var id : String

enum CodingKeys: String, CodingKey {
case source = "_source"
case id = "_id"
}
}

struct Source: Codable {
var nome : String
var endereco : String?
var uf : String?
var cidade : String?
var bairro : String?
}

If you don't want _id altogether then simply remove the related parts.

As for your Alamofire related code, that part is good.


Notable improvements:

  • Have avoided underscored variable name in model by customizing CodingKeys for key mapping purpose
  • Typenames should always start with a Capital letter (so _source is Source)

    • Similarly, variable names should always start with a lowercase letter
  • Made some variables optional (based on your updated response)

    • Keeping a variable non-optional means it must be present in the response for the model to be created
    • Making a variable optional means that key may or may not be present in the response and it not being there won't prevent the model from being created

Problem while converting JSON to Swift The data couldn’t be read because it isn’t in the correct format.

First of all

never print(error.localizedDescription).

in a JSONDecoder catch block. You get a generic but quite meaningless error message.

Always print the entire error, DecodingErrors are very descriptive

print(error)

Your code contains three major errors, one of them (error #3) occurs multiple times

Error #1

typeMismatch(Swift.Dictionary<Swift.String, Any>, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

indicates that the root object is an array, the JSON starts clearly with [

Solution: Decode [CryptListStruct].self


Error #2

dataCorrupted(Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), CodingKeys(stringValue: "volume_1mth_usd", intValue: nil)], debugDescription: "Parsed JSON number <3699822674922524.74> does not fit in Int.", underlyingError: nil))

indicates that the received value 3699822674922524.74 is actually a Double.

Solution: Declare

let volume1MthUsd: Double

Error #3

keyNotFound(CodingKeys(stringValue: "id_icon", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 4", intValue: 4)], debugDescription: "No value associated with key CodingKeys(stringValue: "id_icon", intValue: nil) ("id_icon").", underlyingError: nil))

indicates that the key id_icon is missing (at least) in the 5th item of the array.

Solution: Declare the type as optional

let idIcon : String?

The same error occurs for dataTradeStart, dataTradeEnd, dataQuoteStart, dataQuoteEnd, dataOrderbookStart, dataOrderbookEnd, dataStart, dataEnd

let dataQuoteStart, dataQuoteEnd, dataOrderbookStart, dataOrderbookEnd: String?
let dataTradeStart, dataTradeEnd : String?
let dataStart, dataEnd: String?

Side note:

You can delete the entire CodingKeys enum if you replace assetID with assetId and add the convertFromSnakeCase key decoding strategy

let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let crpytList = try decoder.decode([CryptListStruct].self, from: data)

The data couldn’t be read because it isn’t in the correct format [swift 3]

You're right, problem occurred because of "\n". I tried your code without "\n" and it's work perfectly.

I replaced "\n" by "\\n", and iOS seems to convert the string to dictionary :

let value =  "{\"state\":\"NY\",\"city\":\"NY\",\"postalCode\":\"22002\",\"value\":\"Fifth Avenue1\nNY NY 22002\nUSA\",\"iosIdentifier\":\"71395A78-604F-47BE-BC3C-7F932263D397\",\"street\":\"Fifth Avenue1\",\"country\":\"USA\"}"

if let data = value.replacingOccurrences(of: "\n", with: "\\n").data(using: String.Encoding.utf8) {
do {
let a = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves) as? [String: Any]
NSLog("check \(a)")
} catch {
NSLog("ERROR \(error.localizedDescription)")
}
}

I obtained this in my log :

check Optional(["value": Fifth Avenue1
NY NY 22002
USA, "country": USA, "city": NY, "iosIdentifier": 71395A78-604F-47BE-BC3C-7F932263D397, "street": Fifth Avenue1, "postalCode": 22002, "state": NY])

Swift The data couldn’t be read because it isn’t in the correct format

Your JSON format doesn't quite match what you're trying to decode. You need a wrapper for the latest_photos array at the root of your JSON object.

For example:

struct LatestPhotosWrapper: Codable {
let latestPhotos: [LatestPhoto]

enum CodingKeys: String, CodingKey {
case latestPhotos = "latest_photos"
}
}
let apod = try JSONDecoder().decode(LatestPhotosWrapper.self, from: data)

(Rather than providing a CodingKey, you can also look into the built-in systems for converting from snake case: https://developer.apple.com/documentation/foundation/jsondecoder/keydecodingstrategy/convertfromsnakecase)

Also, you may want to print the error and not just the error.localizedDescription -- you can get a better picture of what's going on. For example, with your original code, you get:

Expected to decode Array but found a dictionary instead.

Finally, you might check out app.quicktype.io -- you can paste in your JSON and get correct Swift structs pre-built for you.

Swift, The data couldn’t be read because it isn’t in the correct format

The way you making your model is wrong, your're not assigning default value in case of nil or error. So decoding always failed.

try to create model this way or use online tools:

struct ResultRootModel : Codable {
let results : [ResultsModel]?
let page : Int?
let total_results : Int?
let total_pages : Int?

enum CodingKeys: String, CodingKey {

case results = "results"
case page = "page"
case total_results = "total_results"
case total_pages = "total_pages"
}

init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
results = try values.decodeIfPresent([ResultsModel].self, forKey: .results)
page = try values.decodeIfPresent(Int.self, forKey: .page)
total_results = try values.decodeIfPresent(Int.self, forKey: .total_results)
total_pages = try values.decodeIfPresent(Int.self, forKey: .total_pages)
}

}


struct ResultsModel : Codable {
let video : Bool?
let vote_average : Double?
let popularity : Double?
let vote_count : Int?
let release_date : String?
let adult : Bool?
let backdrop_path : String?
let overview : String?
let genre_ids : [Int]?
let title : String?
let original_language : String?
let original_title : String?
let poster_path : String?
let id : Int?

enum CodingKeys: String, CodingKey {

case video = "video"
case vote_average = "vote_average"
case popularity = "popularity"
case vote_count = "vote_count"
case release_date = "release_date"
case adult = "adult"
case backdrop_path = "backdrop_path"
case overview = "overview"
case genre_ids = "genre_ids"
case title = "title"
case original_language = "original_language"
case original_title = "original_title"
case poster_path = "poster_path"
case id = "id"
}

init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
video = try values.decodeIfPresent(Bool.self, forKey: .video)
vote_average = try values.decodeIfPresent(Double.self, forKey: .vote_average)
popularity = try values.decodeIfPresent(Double.self, forKey: .popularity)
vote_count = try values.decodeIfPresent(Int.self, forKey: .vote_count)
release_date = try values.decodeIfPresent(String.self, forKey: .release_date)
adult = try values.decodeIfPresent(Bool.self, forKey: .adult)
backdrop_path = try values.decodeIfPresent(String.self, forKey: .backdrop_path)
overview = try values.decodeIfPresent(String.self, forKey: .overview)
genre_ids = try values.decodeIfPresent([Int].self, forKey: .genre_ids)
title = try values.decodeIfPresent(String.self, forKey: .title)
original_language = try values.decodeIfPresent(String.self, forKey: .original_language)
original_title = try values.decodeIfPresent(String.self, forKey: .original_title)
poster_path = try values.decodeIfPresent(String.self, forKey: .poster_path)
id = try values.decodeIfPresent(Int.self, forKey: .id)
}

}

change this line

let json = try JSONDecoder().decode(result.self, from: datos) 

to

let json = try JSONDecoder().decode(ResultRootModel.self, from: datos)

Some suggestion:

start Struct/Class name with Uppercase

print print(error) instead debugPrint(error.localizedDescription) it will give more explanation on error like below:

keyNotFound(CodingKeys(stringValue: "budget", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "results", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"budget\", intValue: nil) (\"budget\").", underlyingError: nil))

How to exclude properties from Swift Codable?

The list of keys to encode/decode is controlled by a type called CodingKeys (note the s at the end). The compiler can synthesize this for you but can always override that.

Let's say you want to exclude the property nickname from both encoding and decoding:

struct Person: Codable {
var firstName: String
var lastName: String
var nickname: String?

private enum CodingKeys: String, CodingKey {
case firstName, lastName
}
}

If you want it to be asymmetric (i.e. encode but not decode or vice versa), you have to provide your own implementations of encode(with encoder: ) and init(from decoder: ):

struct Person: Codable {
var firstName: String
var lastName: String

// Since fullName is a computed property, it's excluded by default
var fullName: String {
return firstName + " " + lastName
}

private enum CodingKeys: String, CodingKey {
case firstName, lastName, fullName
}

// We don't want to decode `fullName` from the JSON
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
firstName = try container.decode(String.self, forKey: .firstName)
lastName = try container.decode(String.self, forKey: .lastName)
}

// But we want to store `fullName` in the JSON anyhow
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(firstName, forKey: .firstName)
try container.encode(lastName, forKey: .lastName)
try container.encode(fullName, forKey: .fullName)
}
}

The Data Couldn't Be Read Because It Isn't in The Correct Format?

I strongly suggest to learn how to debug: it includes where to look, what info to get, where to get them, etc, and at the end, fix it.

That's a good thing that you print the error, most beginner don't.

print("Failed to create JSON with error: ", error.localizedDescription)

=>

print("Failed to create JSON with error: ", error)

You'll get a better idea.

Second, if it failed, print the data stringified. You're supposed to have JSON, that's right. But how often do I see question about that issue, when it fact, the answer wasn't JSON at all (the API never stated it will return JSON), the author were facing an error (custom 404, etc.) and did get a XML/HTML message error etc.

So, when the parsing fails, I suggest to do:

print("Failed with data: \(String(data: data, encoding: .utf8))")

Check that the output is a valid JSON (plenty of online validators or apps that do that).

Now:

I'm pretty sure my model is correct based on my data,

Well, yes and no.

Little tip with Codable when debuting (and not using nested stuff): Do the reverse.

Make your struct Codable if it's not the case yet (I used Playgrounds)

struct Call: Codable {
let clientref: Int?
let type: String?
}


do {
let calls: [Call] = [Call(clientref: 1, type: "breakfast"),
Call(clientref: 0, type: "lunch"),
Call(clientref: 2, type: "dinner")]

let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted]
let jsonData = try encoder.encode(calls)
let jsonStringified = String(data: jsonData, encoding: .utf8)
if let string = jsonStringified {
print(string)
}
} catch {
print("Got error: \(error)")
}

Output:

[
{
"clientref" : 1,
"type" : "breakfast"
},
{
"clientref" : 0,
"type" : "lunch"
},
{
"clientref" : 2,
"type" : "dinner"
}
]

It doesn't look like. I could only used an array to put various calls inside a single variable, and that's what you meant for decoding, because you wrote [Call].self, so you were expecting an array of Call. We are missing the "1596218400" parts. Wait, could it be a dictionary at top level? Yes. You can see the {} and the fact it uses "keys", not listing one after the others...

Wait, but now that we printed the full error, does it make more sense now?

typeMismatch(Swift.Array<Any>, 
Swift.DecodingError.Context(codingPath: [],
debugDescription: "Expected to decode Array<Any> but found a dictionary instead.",
underlyingError: nil))

Fix:

let dictionary = try JSONDecoder().decode([String: Call].self, from: data)
completion(dictionary.values) //since I guess you only want the Call objects, not the keys with the numbers.


Related Topics



Leave a reply



Submit