Swift не может получить доступ к части ответа JSON с инициализатором enum - PullRequest
0 голосов
/ 16 марта 2019

У меня проблемы с доступом к части ответа 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 имеет значение «false»

Я использую веб-сайт: 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 Ответ

0 голосов
/ 16 марта 2019

Прежде всего: если вы отвечаете за серверную часть, отправьте 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
...