【问题标题】:How to decode JSON with codable in Swift 4?如何在 Swift 4 中使用可编码的方式解码 JSON?
【发布时间】:2019-04-26 21:00:48
【问题描述】:

我在用 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\").", 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)

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

【问题讨论】:

  • 不要使用try!。编写一个 try/catch 块并记录您遇到的错误。
  • 数组和字典也可以写[Variety].self,我们不经常使用通用语法Array&lt;Variety&gt;
  • 用Try,错误是“数据丢失,无法读取。”

标签: json swift codable


【解决方案1】:

对于每个可选值,您必须使用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,以便编译器能够识别它们。

【讨论】:

  • 我认为你的意思是每个可选的 KEY 都需要 decodeIfPresent。可选值只需将它们标记为可选即可处理。
  • @smileBot 真的。但是,我会一直使用 decodeIfPresent,因为许多 JSON 编码器会删除带有 null 值的键,而理智的解码器应该可以处理这两种情况。
  • 这是一个聪明的观点。实际上,我想知道如果它从对象中删除 nil 键,是否将 JSON 编码对象保存到用户默认值。我将不得不对此进行测试,因为我即将在我的项目中这样做!
  • @smileBot Generated Encodable 使用 encodeIfPresent(_:forKey:) 作为可选值并且不编码 null。您可以使用自定义实现强制编码null
  • @smileBot 不,你没有。您可以使用encode(optional, forKey: key)。生成的编码器使用encodeIfPresent,它不包括null 值。如果您编写自定义编码并使用encode,则null 将被保留。绝对不要在 Swift 中使用 NSNull :)
猜你喜欢
  • 2021-01-26
  • 2018-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-11
  • 2018-06-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多