如何使用 Swift 的 Decodable 解析任意 JSON 字符串,而您只知道或关心几个字段? [关闭]

Posted

技术标签:

【中文标题】如何使用 Swift 的 Decodable 解析任意 JSON 字符串,而您只知道或关心几个字段? [关闭]【英文标题】:How can one use Swift's Decodable to parse an arbitrary JSON string where you only know or care about a few fields? [closed] 【发布时间】:2018-06-04 17:21:34 【问题描述】:

新的 Swift“解码器”类听起来是解析 JSON 数据的好方法,但我发现的所有示例都使用了一个众所周知的、定义明确的“结构”。

在我的情况下,我正在查询一个返回巨大 JSON 字符串的任意网站,我只关心一些(深度嵌套的)字段,所以我不想花所有时间来定义一个“结构” ' 来对付他们。

甚至可以用“解码器”来做到这一点吗?如果是这样,如何去做?

【问题讨论】:

【参考方案1】:

这个问题似乎是基于对 Decodable 工作原理的误解。为方便起见,Decodable 愿意在幕后进行一些自动代码生成,以便您可以定义一个结构或结构嵌套并解码整个 JSON。但是您不需要利用它来解码 JSON。

无需为您不关心的“字段”定义结构属性。如果一个 JSON 字典包含 100 个键,而你对应的结构只包含一个属性,没问题;将获取该密钥,而不会获取其他密钥。

关于“深度嵌套”部分,编写简单的嵌套结构应该不会花费您太多时间来执行潜水以找到您真正关心的字典。但是,如果您甚至不想这样做,您可以编写一个 init(from:) 的实现,它会深入并获取所需的值。

换句话说,如果您认为 Decodable 主要由您的init(from:) 实现组成,并学习编写它需要的代码,您将看到这个 JSON 可以被解析为几行简单的代码。

作为一个例子,下面是一个深度嵌套的信息的 JSON 草图,其中包含我们想要忽略的每个级别的一堆额外信息:


  "ignore": true,
  "outer1": 
    "ignore": true,
    "outer2": 
      "ignore": true,
      "outer3": 
        "name": "matt",
        "ignore": true
      
    
  

我想做的是定义一个非常简单的结构体 Person 仅由深度嵌套的 name 组成:

struct Person : Decodable 
    let name : String

我能做到!为此,我自己实现了 Decodable,提供了一个“hoover” CodingKey 采用者结构和一个init(from:) 的实现,就像这样(这可能看起来需要做很多工作,但实际上并非如此,因为 AnyCodingKey 实现是样板文件,从here复制粘贴,init(coder:)的实现只是几行代码,很容易写):

    struct Person : Decodable 
        let name : String
        struct AnyCodingKey : CodingKey 
            var stringValue: String
            var intValue: Int?
            init(_ codingKey: CodingKey) 
                self.stringValue = codingKey.stringValue
                self.intValue = codingKey.intValue
            
            init(stringValue: String) 
                self.stringValue = stringValue
                self.intValue = nil
            
            init(intValue: Int) 
                self.stringValue = String(intValue)
                self.intValue = intValue
            
        
        init(from decoder: Decoder) throws 
            var con = try! decoder.container(keyedBy: AnyCodingKey.self)
            con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer1"))
            con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer2"))
            con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer3"))
            let name = try! con.decode(String.self, forKey: AnyCodingKey(stringValue:"name"))
            self.name = name
        
    

当我想深入 JSON 并获取 name 信息时,这很简单:

let person = try! JSONDecoder().decode(Person.self, from: json)

结果是一个带有name"matt" 的Person 对象。请注意,我不需要添加任何 ignore 键,也不需要制作结构嵌套。

【讨论】:

【参考方案2】:

当然你可以做到这一点,但同时使用 JSonSerializationDecodable ,你必须序列化 json 直到达到所需的内容然后解码它,但我建议只为根键创建结构然后解码,想想因为它是从上到下的路径,所以不要解码不在所需内容路径中的密钥

【讨论】:

以上是关于如何使用 Swift 的 Decodable 解析任意 JSON 字符串,而您只知道或关心几个字段? [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 4 中使用 Decodable 解析 JSON

在 Swift 4 中使用 Decodable 和 CodingKeys 解析 JSON

处理包含多种类型的 JSON 数组 - Swift 4 Decodable

使用 Swift 4 Decodable 将字符串 JSON 响应转换为布尔值

使用 Decodable 进行 JSON 解析时 optional 和 decodeIfPresent 有啥区别?

使用 Decodable 进行 JSON 解析时 optional 和 decodeIfPresent 有啥区别?