如果我不确定密钥,我应该如何使用 JSONDecoder 解码 json object

How should I decode a json object using JSONDecoder if I am unsure of the keys

我有一个 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 是在其他地方生成的,并不总是这些值,它们总是唯一的,但基于发生在产品其他地方的逻辑,因此对不同内容文章的另一个调用可以 return leftBodysubTitle

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

我需要能够访问其他地方代码中每个键的 body。不过,另一个 API 回复会告诉我所需的密钥。

我不确定如何用 Decodable 处理这个问题,因为我无法提前输入值。

我曾考虑过为 body -

建模之类的东西
struct ContentArticleTextEntries: Decodable {
    var id: String
    var text: String
    var locale: Locale
}

并将值存储在类似 -

的结构中
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"] 那样访问。

我也不知道有没有更好的方法来处理这个问题。

我曾考虑过使用枚举将键转换为 'type',但同样无法提前了解枚举情况。

我知道 textEntries 这不会改变,我知道 id、text 和 locale 这不会改变。它是我不知道的这一层之间的键。我已经尝试了@vadian 发布的有用的解决方案,但似乎无法在只需要解码一组密钥的情况下进行这项工作。

使用 "decodeIfPresent" 变体方法而不是解码方法,您还需要将 ContentArticleTextEntries 字典分解为单独的键:

struct ContentArticle: Decodable {
    var id: String
    var text: String?
    var locale: String?

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

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.id = try container.decodeIfPresent(String.self, forKey: .id) ?? ""
        self.text = try container.decodeIfPresent(String.self, forKey: .text)
        self.locale = try container.decodeIfPresent(String.self, forKey: .locale) 
    }

}

如果你的模特是这样的,

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.[=15时如果没有特殊处理,不用写enum CodingKeysinit(from:) =]

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

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