Swift 无法使用枚举初始值设定项访问部分 JSON 响应

Swift can't access part of JSON response with enum initializer

我无法访问 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
                    }
                 ]
              }
           ]
        }
     }
   }
 }
}

方法 如您所见,源可以有多个类别。单个类别将具有 'name'、'description',并且可能具有 'children'。 Children 将有 'name'、'description',也可能有 'children'。这可能会没完没了。如果没有 children SJON 语句是 'false'

我使用网站:https://app.quicktype.io 生成一堆代码来解析 JSON。我修改了结果,因为网站不知道children可以无穷无尽:

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!)

这部分工作正常。我可以访问 "New Born",但无法访问他的 children。 我搜索了很长时间我尝试了这么多的答案。在这里分享很多。我希望获得的访问权限是:

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

但这会给我一个错误: "Cannot call value of non-function type 'EnChildren'"

还有这样的代码:

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

我哪儿也去不了。

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

首先:如果你负责服务器端,为叶子节点发送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