如何在Swift 4中使用可编码的方式解码JSON?

时间:2018-11-24 16:55:43

标签: json swift codable

我在使用Codable解码JSON时遇到了大问题

我得到了错误

  

线程1:致命错误:“尝试!”表达式意外引发错误:Swift.DecodingError.keyNotFound(SpecieKeys(stringValue:“ v”,intValue:nil),Swift.DecodingError.Context(codingPath:[_JSONKey(stringValue:“ Index 0”,intValue:0)],debugDescription :“没有与键SpecieKeys(stringValue:\” v \“,intValue:nil)(\” v \“)。(\” v \“)。”相关联的值,underlyingError:nil))

我花了很多时间,但我不明白为什么..:-(

这是我的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)!

和我的结构

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)
}
}

最后一个代码

var jsonResult = [Specie]()

jsonResult = try! JSONDecoder().decode(Array<Specie>.self, from: jsonDATA)

有人可以帮助我解决我的错误。

1 个答案:

答案 0 :(得分:2)

对于每个可选值,您必须使用decodeIfPresent(:forKey) 而不是decode( :forKey)decode(_:forKey)在找到nil值("No value associated with key ...")时将无法解析

但是,更简单的解决方案是让编译器生成您的解码初始化程序:

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"
        }
    }
}

唯一需要做的就是将您的键枚举重命名为CodingKeys,以便编译器可以识别它们。