Swift Decodable - bagaimana menghindari tipe generik?

Aug 19 2020

Saya mengambil objek bersarang yang kompleks dari JSON REST API saya.

DocumentDraft
 - uuid: String
 - schema: Schema // Very complicated object with many variations
 - url: String
 - values: [Value]
 - successors: [String]
 - predecessors: [String]

Value
 - key: String
 - val: String? OR [String]?   // <-- This is the problem

Saya kira cara yang tepat untuk mengatasinya adalah dengan memperkenalkan tipe generik.

struct Value<V: Decodable>: Decodable {
  let key: String
  let val: V?
}

... tapi meski begitu, valuesbisa menjadi array campuran, jadi saya tidak melihat bagaimana menyatakan apa Vyang akan membantu.

Tapi kemudian, tentu saja tipe generik menyebar ke seluruh hierarki, ke DocumentDraftobjek, ke penerbit, ke panggilan API saya, dll. Mencemari seluruh rantai panggilan dan objek yang sangat bersih dan dapat dibaca. Saya ingin menangani ini hanya pada level Value, dan biarkan JSONDecoder mengembalikan salah satu dari keduanya.

Apakah ada cara lain untuk menangani dua kemungkinan opsional valsebagai salah satu Stringatau [String]tanpa mengubah seluruh objek induk?

Jawaban

4 gcharita Aug 19 2020 at 12:31

Anda dapat mencapainya hanya dengan menggunakan [String]tipe dan secara manual mengimplementasikan init(from:)fungsi Decodableprotokol seperti ini:

struct Value: Decodable {
    let key: String
    let val: [String]?
    
    enum CodingKeys: String, CodingKey {
        case key, val
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        
        key = try container.decode(String.self, forKey: .key)
        do {
            if let string = try container.decodeIfPresent(String.self, forKey: .val) {
                val = [string]
            } else {
                val = nil
            }
        } catch DecodingError.typeMismatch {
            val = try container.decodeIfPresent([String].self, forKey: .val)
        }
    }
}

Ketika decoding ke Stringnilai berhasil, buat array string hanya dengan satu elemen. Ketika decoding ke Stringnilai gagal, coba decode sebagai[String]