Swift Decodable - จะหลีกเลี่ยงประเภททั่วไปได้อย่างไร?

Aug 19 2020

ฉันกำลังดึงอ็อบเจ็กต์ที่ซ้อนกันซับซ้อนจาก JSON REST API ของฉัน

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

ฉันคิดว่าวิธีที่เหมาะสมในการจัดการกับสิ่งนี้คือการแนะนำประเภททั่วไป

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

... แต่ถึงอย่างนั้นvaluesอาจเป็นอาร์เรย์ผสมได้ดังนั้นฉันจึงไม่เห็นว่าการประกาศว่าอะไรVจะช่วยได้อย่างไร

แต่แน่นอนว่าประเภททั่วไปจะแพร่กระจายไปทั่วทั้งลำดับชั้นไปยังDocumentDraftวัตถุไปยังผู้เผยแพร่ไปจนถึงการเรียก API ของฉัน ฯลฯ ซึ่งก่อให้เกิดมลพิษต่อห่วงโซ่ทั้งหมดของการเรียกและวัตถุที่สะอาดและอ่านได้ ฉันต้องการจัดการกับสิ่งนี้เฉพาะในระดับValueและปล่อยให้ JSONDecoder ส่งคืนหนึ่งในสองอย่างนั้น

มีวิธีอื่นในการจัดการกับความเป็นไปได้สองประการของตัวเลือกที่valเป็นStringหรือ[String]ไม่เปลี่ยนวัตถุแม่ทั้งหมดหรือไม่

คำตอบ

4 gcharita Aug 19 2020 at 12:31

คุณสามารถทำได้โดยใช้เฉพาะ[String]ประเภทและใช้init(from:)ฟังก์ชันของDecodableโปรโตคอลด้วยตนเองเช่นนี้:

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

เมื่อถอดรหัสเป็นStringค่าสำเร็จให้สร้างอาร์เรย์ของสตริงที่มีองค์ประกอบเดียวเท่านั้น เมื่อการถอดรหัสเป็นStringค่าล้มเหลวให้ลองถอดรหัสเป็น[String]