【问题标题】:Swift 4 Decodable multiple containersSwift 4 可解码多个容器
【发布时间】:2018-07-02 19:34:04
【问题描述】:

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

 {
     "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 containers swift4 codable decodable


    【解决方案1】:

    阅读 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 中并查看。

    【讨论】:

    • 嘿瓦迪安!感谢您的回复和周到的回答。我以前做过。我遇到的主要问题是“结果”对象属于“根”类。因此,为了访问子对象,我需要为每个属性访问 result.graphql.shortcode_media.id,然后构造一个新的“媒体”对象。在 Mark answer 中(不是接受的答案,附在 OP 上),他表明使用容器并覆盖“decode”init,我们最终调用 .decode(Media.self),我们得到一个“Media”对象作为回报。是否认为这是一种更好的做法,还是我错过了重点?再次感谢您,先生!
    • 这是一个品味问题。我更新了答案,添加了一个惰性实例化属性以直接从root 对象访问Media 对象。
    • 嘿 Vadian,很抱歉没有接受您的回答(我希望我能接受)。它给了我很多见解,马克回答(口味问题),对于具体的项目更合适。我一定会在不同的项目中使用这种方法。再次感谢您!
    • 不用担心。那不是竞争。 给猫剥皮的方法不止一种 ?
    • 嘿瓦迪安!经过几周的尝试,我认为您的解决方案更好,因为不需要容器编写。小问题,将所有子属性扁平化为一个对象的最佳方法是什么?因此,如果我有更多容器等,最终我将需要一个拥有这些属性的“发布”对象。你会推荐什么?
    【解决方案2】:

    正如 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
    
    }
    

    【讨论】:

    • 嗨,Rob,感谢您的回复。 Vadian 是正确的,我误解了 JSON 中的关系。在这种情况下,自定义解码器更适合我。正如我在 Vadian 答案中的评论中提到的那样,我最后正在寻找一个 Post 对象,让我更容易与应用程序的其余部分进行交互。再次感谢您!
    • 如果有嵌套数组,我们怎么能这样解码。 {“graphql”:{“shortcode_media”:({“id”:“1657677004214306744”,“shortcode”:“BcBQHPchwe4”},{“id”:“xyzzy”,“shortcode”:“abc”})}}
    • 那不是有效的 JSON;我假设您的意思是([。但它是一样的。您只需解码一个数组而不是单个值。但是,如果您遇到问题,这是一个新问题,您应该在搜索解码嵌套数组后为它打开一个新问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 2018-02-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多