Swift 4个可解码多个容器

Swift 4 Decodable multiple containers

我想了解如何将这个多容器 JSON 解析为一个对象。我有 (马克回答),但他解释了如何使用一级容器解决它。出于某种原因,我无法模仿多个容器的行为。

 {
     "graphql": {
        "shortcode_media": {
          "id": "1657677004214306744",
          "shortcode": "BcBQHPchwe4"
        }
     }
  }

class Post: Decodable {


    enum CodingKeys: String, CodingKey {
        case graphql // The top level "user" key
        case shortcode_media
    }

    enum PostKeys: String, CodingKey {
        case id
    }

    required init(from decoder: Decoder) throws {

        let values = try decoder.container(keyedBy: CodingKeys.self)

        let post = try values.nestedContainer(keyedBy: PostKeys.self, forKey: .shortcode_media)

        self.id = try post.decode(String.self, forKey: .id)

    }

    var id: String

}

我得到:

Swift.DecodingError.Context(codingPath: [], debugDescription: "Cannot get KeyedDecodingContainer<PostKeys> -- no value found for key \"shortcode_media\"", underlyingError: nil))

任何帮助将不胜感激,谢谢!

阅读JSON。

任何开口 { 都是准分隔符。 JSON 的缩进也表示层次结构。

为了清楚起见,我删除了所有编码键并保留了变量名——应该是 camelCased——不变。

struct Root : Decodable {
    let graphql : Graph

    // to access the `Media` object declare a lazy instantiated property

    lazy var media : Media = {
        return graphql.shortcode_media
    }()
}

struct Graph : Decodable {
    let shortcode_media : Media
}

struct Media : Decodable {
    let id: String
    let shortcode : String
}

let jsonString = """
{
    "graphql": {
        "shortcode_media": {
            "id": "1657677004214306744",
            "shortcode": "BcBQHPchwe4"
        }
    }
}
"""

do {       
    let data = Data(jsonString.utf8) 
    var result = try decoder.decode(Root.self, from: data)
    print(result.media)
} catch {
    print("error: ", error)
}

使用 nestedContainer 编写自定义初始化程序比创建实际层次结构更费力。

请将整个代码粘贴到 Playground 中并查看。

正如 vadian 所说,您没有匹配 JSON 结构。顶层没有 shortcode_media 密钥,就像您在 CodingKeys.

中编码的那样

为了使用自定义解码器对其进行解码,您需要遍历每个级别并进行处理。

class Post: Decodable {

    enum CodingKeys: String, CodingKey {
        case graphql
    }

    enum GraphQLKeys: String, CodingKey {
        case shortcode_media
    }

    enum PostKeys: String, CodingKey {
        case id
    }

    required init(from decoder: Decoder) throws {
        // unload the top level
        let container = try decoder.container(keyedBy: CodingKeys.self)
        // Unload the graphql key
        let graphql = try container.nestedContainer(keyedBy: GraphQLKeys.self, forKey: .graphql)
        // unload the shortcode_media key
        let post = try graphql.nestedContainer(keyedBy: PostKeys.self, forKey: .shortcode_media)

        // Finally, unload the actual object
        self.id = try post.decode(String.self, forKey: .id)
    }

    var id: String

}