如果不确定密钥,如何使用JSONDecoder解码json对象

时间:2019-11-20 10:25:49

标签: json swift codable decodable jsondecoder

我有以下形状的api响应-

  {
   "textEntries":{
      "summary":{
         "id":"101e9136-efd9-469e-9848-132023d51fb1",
         "text":"some text",
         "locale":"en_GB"
      },
      "body":{
         "id":"3692b0ec-5b92-4ab1-bc25-7711499901c5",
         "text":"some other text",
         "locale":"en_GB"
      },
      "title":{
         "id":"45595d27-7e06-491e-890b-f50a5af1cdfe",
         "text":"some more text again",
         "locale":"en_GB"
      }
   }
}

我想通过JSONDecoder对此进行解码,以便可以使用属性。我面临的挑战是密钥,在这种情况下,summarybodytitle是在其他位置生成的,并不总是这些值,它们始终是唯一的,但是基于产品其他位置发生的逻辑,因此对其他内容文章的另一次调用可能返回leftBodysubTitle等。

这些道具的主体模型始终是相同的,但是,我可以期望在任何响应组合中都存在相同的字段。

我将需要能够在其他地方访问代码中每个键的主体。另一个API响应会告诉我我需要的密钥。

我不确定如何使用Decodable处理此操作,因为我无法提前输入值。

我曾考虑过对人体建模-

struct ContentArticleTextEntries: Decodable {
    var id: String
    var text: String
    var locale: Locale
}

并将值存储在--p这样的结构中

struct ContentArticle: Decodable {
    var textEntries: [String: ContentArticleTextEntries]


    private enum CodingKeys: String, CodingKey {
        case textEntries
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.textEntries = try values.decode(ContentArticleTextEntries.self, forKey: .textEntries)

    }

}

我可以让他们在其他地方使用下标来访问属性,但是我不知道如何解码为这种形状,因为上面的方法不起作用。

所以我以后会像textEntries["body"]这样访问。

我也不知道是否有更好的方法来处理此问题。

我曾考虑过使用枚举将键转换为“类型”,但同样由于事先不知道枚举的情况,因此无法实现。

我知道textEntries不会更改,并且我知道id,text和locale不会更改。我不知道这是这层之间的关键。我尝试了@vadian发布的有用的解决方案,但似乎在仅需要解码一组密钥的情况下无法使这项工作有效。

3 个答案:

答案 0 :(得分:1)

对于this answer中提出的解决方案,结构是

struct ContentArticleTextEntries: Decodable {
    let title : String
    let id: String
    let text: String
    let locale: Locale

    enum CodingKeys: String, CodingKey {
        case id, text, locale
    }

    init(from decoder: Decoder) throws {
        self.title = try decoder.currentTitle()
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decode(String.self, forKey: .id)
        self.text = try container.decode(String.self, forKey: .text)
        let localeIdentifier = try container.decode(String.self, forKey: .locale)
        self.locale = Locale(identifier: localeIdentifier)
    }
}     

struct ContentArticle: TitleDecodable {
    let title : String
    var elements: [ContentArticleTextEntries]
}

struct Container: Decodable {
    let containers: [ContentArticle]
    init(from decoder: Decoder) throws {
        self.containers = try decoder.decodeTitledElements(ContentArticle.self)
    }
}

然后解码Container.self

答案 1 :(得分:0)

使用“ decodeIfPresent”变体方法代替解码方法,您还需要将ContentArticleTextEntries字典分解为单独的键:

const Account = require('../../model/account')

答案 2 :(得分:0)

如果您的模特很喜欢,

struct ContentArticle: Decodable {
    let textEntries: [String: ContentArticleTextEntries]
}

struct ContentArticleTextEntries: Decodable {
    var id: String
    var text: String
    var locale: String
}

然后,您可以像这样简单地基于key访问数据,

let response = try JSONDecoder().decode(ContentArticle.self, from: data)
let key = "summary"
print(response.textEntries[key])

注意:如果在解析JSON时没有特殊处理,则无需编写enum CodingKeysinit(from:)