Swift 4 Codable Decoding JSON

When decoding a Codable struct from JSON, how do you initialize a property not present in the JSON?

You can simply use coding keys, like this:

struct Reminder: Identifiable, Codable {
var id: String = UUID().uuidString
var title: String
var dueDate: Date
var notes: String? = nil
var isComplete: Bool = false

enum CodingKeys: String, CodingKey {
case title
case dueDate
case notes
case isComplete
}
}

That way, the only properties that will be encoded/decoded are the ones that have a corresponding coding key.

Swift 4 Codable decoding json

If your JSON has a structure

{"themes" : [{"title": "Foo", "answer": 1, "question": 2},
{"title": "Bar", "answer": 3, "question": 4}]}

you need an equivalent for the themes object. Add this struct

struct Theme : Codable {
var themes : [Question]
}

Now you can decode the JSON:

if let decoded = try? JSONDecoder().decode(Theme.self, from: data) {
print("decoded:", decoded)
} else {
print("Not working")
}

The containing Question objects are decoded implicitly.

Swift Codable JSON parse error with JSONDecoder

You have several mistakes on defining object. For example you have defined id, msupply is a integer but they are string. You have defined the volume24 and volume24a is string but they are not. You need to fix all of these probably thats why you can't parse it.

All wrong defined parameters are id, mSupply, volume24, volume24a, tSupply for coin object and for the info object you need to convert time to Int aswell.

Parse JSON from file using Codable swift

You are trying to decode a type of Cities.self, but your JSON is an array - it starts with "[" and ends with "]". You might want to try declaring the type [Cities].self in your decoder call.

let jsonDescription = try JSONDecoder().decode([City].self, from: data)

Edit:

@Joakim_Danielson caught an issue, you need to decode the array [City] and not [Cities], because that's what your JSON is (I edited the above line of code accordingly).

But there is another issue: the property coord in your struct City is declared as an array [Coordinates]?, but your JSON does not have an array in the "coord" key - just a single instance.

Try changing your coord property, make it a type of Coordinate?.

Using Codable to encode/decode from Strings to Ints with a function in between

For Codable encoding and decoding that requires custom transformation type stuff, like that, you just need to implement the initializer and encode methods yourself. In your case it would look something like this. It's a bit verbose just to try to get the idea across really clearly.

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
let hexAString: String = try container.decode(String.self, forKey: .hexA)
self.hexA = hexAConversion(from: hexAString)
let hexBString: String = try container.decode(String.self, forKey: .hexB)
self.hexB = hexBConversion(from: hexBString)
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
//Assuming you have another set of methods for converting back to a String for encoding
try container.encode(self.name, forKey: .name)
try container.encode(hexAStringConversion(from: self.hexA), forKey: .hexA)
try container.encode(hexBStringConversion(from: self.hexB), forKey: .hexB)
}

Hot to decode JSON data that could and array or a single element in Swift?

Your JSON actually is either a String or an array of Strings. So you need to create a custom decoder to decode and then convert them to Double:

struct Info {
let author, title: String
let tags: [Tags]
let price: [Double]
enum Tags: String, Codable {
case nonfiction, biography, fiction
}
}

extension Info: Codable {
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
author = try container.decode(String.self, forKey: .author)
title = try container.decode(String.self, forKey: .title)
tags = try container.decode([Tags].self, forKey: .tags)
do {
price = try [Double(container.decode(String.self, forKey: .price)) ?? .zero]
} catch {
price = try container.decode([String].self, forKey: .price).compactMap(Double.init)
}
}
}

Playground testing

let infoData = Data("""
{
"author" : "Mark A",
"title" : "The Great Deman",
"tags" : [
"nonfiction",
"biography"
],
"price" : "242"

}
""".utf8)
do {
let info = try JSONDecoder().decode(Info.self, from: infoData)
print("price",info.price) // "price [242.0]\n"
} catch {
print(error)
}

let infoData2 = Data("""
{
"author" : "Mark A",
"title" : "The Great Deman",
"tags" : [
"nonfiction",
"biography"
],
"price" : [
"242",
"299",
"335"
]

}
""".utf8)

do {
let info = try JSONDecoder().decode(Info.self, from: infoData2)
print("price",info.price) // "price [242.0, 299.0, 335.0]\n"
} catch {
print(error)
}


Related Topics



Leave a reply



Submit