6

Why is the names Array not decoding?

Prepared for Playground, Simple paste this into your playground

import Foundation

struct Country : Decodable {

    enum CodingKeys : String, CodingKey {
        case names
    }

    var names : [String]?
}

extension Country {
    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        names = try values.decode([String]?.self, forKey: .names)!
    }
}

let json = """
 [{
    "names":
      [
       "Andorre",
       "Andorra",
       "アンドラ"
      ]
 },{
    "names":
      [
       "United Arab Emirates",
       "Vereinigte Arabische Emirate",
       "Émirats Arabes Unis",
       "Emiratos Árabes Unidos",
       "アラブ首長国連邦",
       "Verenigde Arabische Emiraten"
      ]
  }]
""".data(using: .utf8)!

let decoder = JSONDecoder()
do {
    let countries = try decoder.decode([Country].self, from: json)
    countries.forEach { print($0) }
} catch {
    print("error")
}
Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
Jan
  • 12,992
  • 9
  • 53
  • 89

1 Answers1

2

You have defined names as an optional property of Country. If your intention is that this key may not be present in the JSON then use decodeIfPresent:

extension Country {
    public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        names = try values.decodeIfPresent([String].self, forKey: .names)
    }
}

This method returns nil if the container does not have a value associated with key, or if the value is null.

But actually you can just omit your custom init(from decoder: Decoder) implementation (and the enum CodingKeys), because that is the default behaviour and will be synthesized automatically.

Remark: An implicit variable error is defined in any catch clause, so

} catch {
    print(error.localizedDescription)
}

can be more informative than just a print("error") (although not in this particular case).

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • Cool this works! But how to solve that for an `Dictionary`. I tried it with `translations = try values.decodeIfPresent(Dictionary.self, forKey: .translations) ` but this raises `The operation couldn’t be completed. (Swift.DecodingError error 1.)`. Do you have for this a solution too? – Jan Jul 10 '17 at 13:41
  • @Jan: As far as I know (but I am not 100% sure) you cannot decode an *arbitrary* dictionary from JSON. Of course if the keys are fixed then you can define a type with those keys as properties: `struct Translations: Decodable { var en: String?; var de: String?; ... }` – Martin R Jul 10 '17 at 14:00
  • I got it working with Dicts: `var translations : [String:String?]? ` and `translations = try values.decodeIfPresent([String:String?].self, forKey: .translations)` – Jan Jul 10 '17 at 17:36