Свойство доступа родительской структуры во вложенной структуре Codable при декодировании дочерней

При использовании декодера во вложенном Codable struct, есть ли способ получить доступ к свойству родительской структуры?

Единственный способ, которым я могу представить, что это может сработать (еще не тестировал), - это использовать ручной декодер в родительской структуре, установив свойство в userInfo словарь, а затем доступ userInfoв дочерней структуре. Но это приведет к появлению большого количества шаблонного кода. Я надеюсь, что есть более простое решение.

struct Item: Decodable, Identifiable {
    let id: String
    let title: String
    let images: Images

    struct Images: Decodable {
        struct Image: Decodable, Identifiable {
            let id: String
            let width: Int
            let height: Int

            init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: CodingKeys.self)
                width = try container.decode(Int.self, forKey: .width)
                height = try container.decode(Int.self, forKey: .height)

                // How do I get `parent.parent.id` (`Item#id`) here?
                id = "\(parent.parent.id)\(width)\(height)"
            }
        }

        let original: Image
        let small: Image
        // …
    }
}

В приведенном выше примере идентификатор элемента, поступающий с сервера, определяется только в свойствах верхнего уровня в JSON, но они мне нужны и в дочерних элементах, поэтому я также могу сделать их Identifiable.

1 ответ

Мне удалось это с помощью предложения Итаи Фербера, упомянутого @New Dev, следующим образом:

  1. Создайте новый ссылочный тип, единственная цель которого - содержать изменяемое значение, которое может передаваться между родительским и дочерним.
  2. Назначьте экземпляр этого типа в словарь userInfo JSONDecoder.
  3. Получите этот экземпляр при декодировании родителя и назначьте ему идентификатор, который вы хотите передать.
  4. Во время декодирования дочернего элемента извлеките этот идентификатор из экземпляра, хранящегося в userInfo ранее.

Я изменил ваш пример выше следующим образом:

struct Item: Decodable, Identifiable {

    enum CodingKeys: String, CodingKey {
        case id
        case title
        case images
    }

    let id: String
    let title: String
    let images: Images

    struct Images: Decodable {
        struct Image: Decodable, Identifiable {
            let id: String
            let width: Int
            let height: Int

            init(from decoder: Decoder) throws {
                let container = try decoder.container(keyedBy: CodingKeys.self)
                width = try container.decode(Int.self, forKey: .width)
                height = try container.decode(Int.self, forKey: .height)

                if let referenceTypeUsedOnlyToContainAChangeableIdentifier = decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] as? ReferenceTypeUsedOnlyToContainAChangeableIdentifier {
                    self.id = referenceTypeUsedOnlyToContainAChangeableIdentifier.changeableIdentifier
                } else {
                    self.id = "something went wrong"
                }
            }
        }

        let original: Image
        let small: Image
        // …

        init(from decoder: Decoder) throws {
            let container = try decoder.container(keyedBy: CodingKeys.self)
            id = try container.decode(String.self, forKey: .id)
            if let referenceTypeUsedOnlyToContainAChangeableIdentifier = decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] as? ReferenceTypeUsedOnlyToContainAChangeableIdentifier {
               referenceTypeUsedOnlyToContainAChangeableIdentifier.changeableIdentifier = id
            }
        }
    }
}

// Use this reference type to just store an id that's retrieved later.
class ReferenceTypeUsedOnlyToContainAChangeableIdentifier {
    var changeableIdentifier: String?
}

// Convenience extension.
extension CodingUserInfoKey {
    static let referenceTypeUsedOnlyToContainAChangeableIdentifier = CodingUserInfoKey(rawValue: "\(ReferenceTypeUsedOnlyToContainAChangeableIdentifier.self)")!
}

let decoder = JSONDecoder()
// Assign the reference type here to be used later during the decoding process first to assign the id in `Item` and then
// later to retrieve that value in `Images`
decoder.userInfo[.referenceTypeUsedOnlyToContainAChangeableIdentifier] = ReferenceTypeUsedOnlyToContainAChangeableIdentifier()
Другие вопросы по тегам