快速解码 JSON 数组或字典错误

Posted

技术标签:

【中文标题】快速解码 JSON 数组或字典错误【英文标题】:decoding JSON array or dictionary error swift 【发布时间】:2018-05-04 21:26:47 【问题描述】:

这是我第一次尝试 Codable/Decodable,我想解码 JSON。我正在尝试访问事件数组中的“名称”和“描述”键。下面是 JSON 的 sn-p - 我在我的代码中遇到了这个错误

“本应解码字典,但找到了一个数组。”

"pagination": 

    "page_number": 1, 
    "page_size": 50, 
    "continuation": "eyJwYWdlIjogMn0", 
    "has_more_items": true
, 

"events": [
    
        "name": 
            "text": "Genesis Part 4", 
            "html": "Genesis Part 4"
        , 

        "description": 
            "text": "Wednesday, June 6-June 27, 2018\n12:00-2:15 PM, Eastern Time\n\u00a0\nCOED\n\u00a0\nBible Study Leader:\u00a0Nicki Cornett\n\u00a0\nContact:NickiCornett@gmail.com\n\u00a0\nGenesis Part 4 -\u00a0Wrestling with God - A Study on Isaac, Jacob, and Esau- Precept Workbook (NASB)\n\u00a0\nGod renews His covenant promise with Abraham through Isaac and Jacob.

这是我进行解码的方式 - (注意 - “描述”还没有出现,因为我在处理事件数组以访问名称和描述时遇到问题

struct Eventbrite: Decodable 

 private enum CodingKeys : String, CodingKey  case events = "events",  name = "name"
    let events: [String:[String]]
    let name: [String:[String]]



   URLSession.shared.dataTask(with: url)  (data, response, error) in

        guard let data = data else return

        do 

        let eventbriteData = try JSONDecoder().decode(Eventbrite.self, from: data)


            print(eventbriteData.name)

【问题讨论】:

【参考方案1】:

name 显然不在paginationevents 的范围内(注意),它是一个常规的[String:String] 字典,可以解码为另一个结构。

解码这个(因为description不完整,我把它省略了),你不需要CodingKeys

struct Eventbrite: Decodable 
    let events: [Event]


struct Event: Decodable 
    let name: Name
    // let description: [String:String]


struct Name : Decodable 
    let text, html : String

【讨论】:

感谢您的回复。您帮助我了解了它的结构以及如何正确解码。一个简单的菜鸟错误,现在更清楚了。谢谢!【参考方案2】:

Right so Decodable 实际上非常聪明,因为您真的不需要编写任何代码来自己进行解码。您只需要确保匹配 JSON 结构(并为任何嵌套对象制作也符合 Decodable 的结构)。换句话说,不要将变量作为字典,而是让它们成为自己的可解码结构。

例如:

struct EventBrite: Decodable 
    let pagination: Pagination
    let events: [Event]


struct Pagination: Decodable 
    let page_number: Int
    let page_size: Int
    let continuation: String
    let has_more_items: Bool


struct Event: Decodable  
     let name: EventName
     let description: EventDescription


struct EventName: Decodable 
    let name: String
    let html: String

等等……

这里重要的一点是,如果不能保证返回键或属性(例如,假设 EventName 并不总是具有从服务器返回的 html 值,您可以轻松地标记该值作为可选。所以类似于:

struct EventName: Decodable 
    let name: String
    let html: String?

另一方面,您实际上弄乱了字典类型声明。您会注意到event 实际上是[String: [String: String]] 类型,因为键是字符串,而值似乎总是字典。名字是[String: String]。这不是您在原始问题中所记录的内容。

当值可能与分页不同时,您需要执行[String: Any] 之类的操作,因此请小心。

但是我认为我建议的方法比将属性作为字典更好。对于一个你不必担心声明它是字典的类型(你在上面犯了一些小错误)。但更重要的是,当每个字典都成为自己明确定义的结构时,您不必担心记住或查找键。点语法/自动完成会自动告诉你可以有什么! (当你的值是 AnyAnyObject 时,不要强制转换!)

也绝对使用结构来完成所有这些工作,因为我曾经对性能进行基准测试,并测量了结构效率,其效率是类的数百万倍。仅供参考。

【讨论】:

感谢您的解释。您进一步帮助我了解了这个概念! 嘿,回头看看这条评论,我现在真的有点困惑,你的最终建议是什么?您能否澄清所有编辑的含义。谢谢 对,所以我的建议是,代替在任何地方使用字典作为属性,创建一个也符合 Decodable 的新结构。所以不必做event["name"] 你可以做event.name。在我看来,它更容易使用,只要属性匹配,Decodable 的魔力就会始终正确解析。另一个好处是,您实际上稍微搞砸了字典的类型,因此您不必担心。例如。正如我所说的名字不是[String:[String]]它的[String: String] 又名。只要属性不是直接的原始类型值(StringBoolInt 等),就会创建一个嵌套更深的新结构。而不是将其保存为字典。 非常感谢您的澄清。这完全有道理,我能够重组并成功运行它

以上是关于快速解码 JSON 数组或字典错误的主要内容,如果未能解决你的问题,请参考以下文章

错误:“应解码 Dictionary<String, Any>,但找到了一个数组。” ——但我还没有定义字典? [复制]

解码错误:typeMismatch“预期解码 Array<Any> 但找到了字典。”

JSON解码致命错误:不能使用stdClass类型的对象作为数组

如何快速反序列化 JSON

当我只想获取字典中的特定值时,如何解码字典值的 JSON 数组?

如何解决从错误的 json 格式解码的问题