API yang saya tangani dengan pengembalian:

{
  "title": "Hello World"
}

Atau

{
  "title": [{
    "text": "Hello World"
  }]
}

Ide saya adalah memiliki struct TitleStringValue yang memiliki dekoder khusus, seperti ini:

struct TitleStringValue: Decodable {
    let text: String
    
    struct TitleStringValueInner: Decodable {
        let text: String
    }
    
    init(from decoder: Decoder) throws {
        if let stringContainer = try? decoder.singleValueContainer() {
            text = try stringContainer.decode(String.self)
        } else {
            var arrayContainer = try decoder.unkeyedContainer()
            text = try arrayContainer.decode(TitleStringValueInner.self).text
            while !arrayContainer.isAtEnd {
                _ = try? arrayContainer.decode(TitleStringValueInner.self)
            }
        }
    }
}

struct MyResult: Decodable {
  let title: TitleStringValue
}

Tetapi dalam kasus di mana judul terdiri dari array, TitleStringValue init(from decoder: Decoder) tidak pernah dipanggil karena decoder gagal sebelumnya, menghadapi array yang mengharapkan tipe nilai tunggal.

Apakah ada cara untuk menyelesaikan ini?

Saya dapat mengimplementasikan decode pada level struct MyResult saya, tetapi itu berarti bahwa setiap struct yang memiliki TitleStringValue membutuhkan dekoder khusus, jadi saya lebih suka mengimplementasikannya pada level TitleStringValue.

1
Kristof Van Landschoot 12 Mei 2021, 14:55

2 jawaban

Jawaban Terbaik

Masalahnya di sini adalah bahwa singleValueContainer dapat digunakan untuk memecahkan kode juga sebuah array. Jadi, kesalahan yang Anda dapatkan dihasilkan oleh try kedua di dalam init(from:) fungsi TitleStringValue dan bukan sebelumnya.

Karena itu, Anda dapat lebih menyederhanakan decoding kustom Anda seperti ini:

struct TitleStringValue: Decodable {
    let text: String
    
    struct TitleStringValueInner: Decodable {
        let text: String
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let string = try? container.decode([TitleStringValueInner].self).first?.text {
            text = string
        } else {
            text = try container.decode(String.self)
        }
    }
}
0
gcharita 12 Mei 2021, 12:29

Saran saya adalah untuk menjaga properti title untuk kedua kasus.

Pertama-tama coba decode String. Jika ini gagal memecahkan kode array Text (saya membuat nama tetap sederhana) dapatkan item pertama dan tetapkan nilai text ke title.

struct Title: Decodable {
    
    struct Text : Decodable { let text : String }
    
    let title : String
    
    private enum CodingKeys : String, CodingKey { case title }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        do {
            title = try container.decode(String.self, forKey: .title)
        } catch DecodingError.typeMismatch {
            let textArray = try container.decode([Text].self, forKey: .title)
            title = textArray.first?.text ?? ""
        }
    }
}
0
vadian 12 Mei 2021, 12:17