Swift 无法使用枚举初始化程序访问部分 JSON 响应

Posted

技术标签:

【中文标题】Swift 无法使用枚举初始化程序访问部分 JSON 响应【英文标题】:Swift can't access part of JSON response with enum initializer 【发布时间】:2019-03-16 19:41:16 【问题描述】:

我无法访问 JSON 响应的一部分。

(部分)来源:


"time":1552726518,
"result":"success",
"errors":null,
"responce":
  "categories":
     "1":
        "nl":
           "name":"New born",
           "description":"TEST",
           "children":[
              
                 "name":"Unisex",
                 "description":"TESTe",
                 "children":[
                    
                       "name":"Pants",
                       "description":"TEST",
                       "children":false
                    
                 ]
              
           ]
        
     
   
 

接近 如您所见,源可以有多个类别。单个类别将有一个“名称”、“描述”并且可能有“子级”。孩子会有一个“名字”、“描述”,也可能有“孩子”。这可能会无穷无尽。如果没有子节点,则 SJON 语句为“假”

我使用网站:https://app.quicktype.io 来生成一个垃圾代码来解析 JSON。我修改了结果,因为网站不知道孩子们可以去无休止:

struct ProductCategories: Codable 
let time: Int?
let result: String?
let errors: [String]?
let responce: ProductCategoriesResponce?

init(time: Int? = nil, result: String? = nil, errors: [String]? = nil, responce: ProductCategoriesResponce? = nil) 

    self.time = time
    self.result = result
    self.errors = errors
    self.responce = responce



struct ProductCategoriesResponce: Codable 
let categories: [String: Category]?


struct Category: Codable 
let nl, en: Children?


struct Children: Codable 
let name, description: String?
let children: EnChildren?


enum EnChildren: Codable 
case bool(Bool)
case childArray([Children])

init(from decoder: Decoder) throws 
    let container = try decoder.singleValueContainer()
    if let x = try? container.decode(Bool.self) 
        self = .bool(x)
        return
    
    if let x = try? container.decode([Children].self) 
        self = .childArray(x)
        return
    
    throw DecodingError.typeMismatch(EnChildren.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Wrong type for EnChildren"))


func encode(to encoder: Encoder) throws 
    var container = encoder.singleValueContainer()
    switch self 
    case .bool(let x):
        try container.encode(x)
    case .childArray(let x):
        try container.encode(x)
    


我可以使用以下方式解码数据:

productCategories = try JSONDecoder().decode(ProductCategories.self, from: jsonData!)

这部分工作正常。我可以访问“新生”,但无法访问他的孩子。 我搜索了很长时间,以寻找我尝试了很多的答案。在这里分享给大家。我期望获得的是:

 if let temp = productCategories.responce?.categories?["\(indexPath.item)"]?.nl?.children! 
 let x = temp(from: Decoder)

但这会给我一个错误: "不能调用非函数类型'EnChildren'的值"

还有类似的代码:

  let temp1 = productCategories.responce?.categories?["\(indexPath.item)"]?.nl?.children

不会带我去任何地方。

那么,有什么想法吗?谢谢。

【问题讨论】:

【参考方案1】:

首先:如果您负责服务器端,请为叶子孩子发送null或省略密钥而不是发送false。它使解码过程变得更加容易。

app.quicktype.io 是一个很好的资源,但我不同意它的建议。

我的解决方案使用带有自定义初始化程序的常规结构 Children。这个结构体可以递归使用。

密钥children 被有条件地解码。先解码[Children],失败解码Bool并将children设置为nil或交出错误。

并且我尽可能地声明了结构成员是非可选的

struct Response: Decodable 
    let time: Date
    let result: String
    let errors: [String]?
    let responce: ProductCategories


struct ProductCategories: Decodable 
    let categories: [String: Category]


struct Category: Decodable 
    let nl: Children


struct Children: Decodable 
    let name, description: String
    let children : [Children]?

    private enum CodingKeys : String, CodingKey  case name, description, children 

    init(from decoder: Decoder) throws 
        let container = try decoder.container(keyedBy: CodingKeys.self)
        name = try container.decode(String.self, forKey: .name)
        description = try container.decode(String.self, forKey: .description)
        do 
            children = try container.decode([Children].self, forKey: .children)
         catch DecodingError.typeMismatch 
            _ = try container.decode(Bool.self, forKey: .children)
            children = nil
        
    

如果您指定适当的日期解码策略,则在根对象中,键 time 可以解码为 Date

let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .secondsSince1970
let result = try decoder.decode(Response.self, from: jsonData!)

例如,您可以使用

访问名称 Unisex
result.responce.categories["1"]?.nl.children?[0].name

【讨论】:

以上是关于Swift 无法使用枚举初始化程序访问部分 JSON 响应的主要内容,如果未能解决你的问题,请参考以下文章

Swift 访问控制

开心档之Swift 访问控制

在swift初始化期间使用枚举设置属性

控制台应用程序无法以域管理员身份读取基于访问的枚举的完整目录列表

Swift - 如何使用枚举为 UITableView 制作自定义标题部分?

Swift之深入解析访问控制权限