Reputation: 57
i have a big problem with decoding JSON with Codable
I got the error
Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.keyNotFound(SpecieKeys(stringValue: "v", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key SpecieKeys(stringValue: \"v\", intValue: nil) (\"v\").", underlyingError: nil))
I spend many time but I don't understand why .. :-(
This is my json
let myJson = """
[{"i":"4","p":"4","l":["Ail"],"ll":["Allium sativum L."]},
{"i":"20.1","l":["Artichaut"],"ll":["Cynara cardunculus"]},
{"i":"XX.3",
"l":["Tomate cerise"],
"ll":["Solanum humboldtii"],
"v":[{"s":1,
"i":"0",
"l":"Orange Grape Tress",
"c":"Orange",
"h":992,
"ss":12
}]
}]
"""
let jsonDATA = myJson.data(using: .utf8)!
and my struct
struct Specie : Decodable {
var id : String?
var name : [String]?
var latinName : [String]?
var varieties : [Variety]?
// keys
enum SpecieKeys: String, CodingKey {
case id = "i"
case name = "l"
case latinName = "ll"
case varieties = "v"
}
struct Variety : Decodable {
var source : Int?
var id : String?
var color : String?
var name : String?
var photo : String?
var harvest : Int?
var semiShelter : Int?
var semiOutside : Int?
// keys
enum VarietyKeys: String, CodingKey {
case id = "i"
case source = "s"
case color = "c"
case photo = "p"
case harvest = "h"
case semiShelter = "ss"
case semiOutside = "so"
case name = "l"
}
init(from decoder: Decoder) throws
{
let vValues = try decoder.container(keyedBy: VarietyKeys.self)
id = try vValues.decode(String.self, forKey: .id)
source = try vValues.decode(Int.self, forKey: .source)
name = try vValues.decode(String.self, forKey: .name)
color = try vValues.decode(String.self, forKey: .color)
photo = try vValues.decode(String.self, forKey: .photo)
harvest = try vValues.decode(Int.self, forKey: .harvest)
semiShelter = try vValues.decode(Int.self, forKey: .semiShelter)
semiOutside = try vValues.decode(Int.self, forKey: .semiOutside)
}
}
init(from decoder: Decoder) throws
{
let sValues = try decoder.container(keyedBy: SpecieKeys.self)
id = try sValues.decode(String.self, forKey: .id)
name = try sValues.decode(Array<String>.self, forKey: .name)
latinName = try sValues.decode(Array<String>.self, forKey: .latinName)
varieties = try sValues.decode(Array<Variety>.self, forKey: .varieties)
}
}
And the last code
var jsonResult = [Specie]()
jsonResult = try! JSONDecoder().decode(Array<Specie>.self, from: jsonDATA)
Someone can help me about my mistake.
Upvotes: 2
Views: 1541
Reputation: 130072
For every optional value you have to use decodeIfPresent(:forKey) instead of decode(:forKey). decode(_:forKey)
will fail your parsing when it finds a nil
value ("No value associated with key ..."
)
However, the easier solution would be to let the compiler generate your decoding initializers:
struct Specie : Decodable {
var id : String?
var name : [String]?
var latinName : [String]?
var varieties : [Variety]?
// keys
enum CodingKeys: String, CodingKey {
case id = "i"
case name = "l"
case latinName = "ll"
case varieties = "v"
}
struct Variety : Decodable {
var source : Int?
var id : String?
var color : String?
var name : String?
var photo : String?
var harvest : Int?
var semiShelter : Int?
var semiOutside : Int?
// keys
enum CodingKeys: String, CodingKey {
case id = "i"
case source = "s"
case color = "c"
case photo = "p"
case harvest = "h"
case semiShelter = "ss"
case semiOutside = "so"
case name = "l"
}
}
}
The only thing necessary is to rename your key enums to CodingKeys
so that the compiler can recognize them.
Upvotes: 3