swift 4 Codable - как декодировать, если есть строка или словарь? - PullRequest
0 голосов
/ 07 января 2019

У меня есть такая структура:

struct OrderLine: Codable{
    let absUrl: String?
    let restApiUrl : String?
    let description : String?
    let quantity : Int?
    let subscription: Subs?
    let total: Double?
 }

struct Subs: Codable{
    let quantity: Int?
    let name: String?
}

и некоторые OrderLine имеют в ответе сервера

"subscription": {
   "quantity": 6,
   "name": "3 Months"
},

но иногда он имеет String тип:

"subscription": "",

без subscription все работает нормально, но с ошибкой

CodingKeys(stringValue: "subscription", intValue: nil)], 
   debugDescription: "Expected to decode Dictionary<String, Any> 
   but found a string/data instead.", underlyingError: nil)

поэтому мой вопрос - как я могу декодировать или String? со значением "", или Subs? без каких-либо ошибок? постскриптум если я расшифрую его как String?, то получу ошибку debugDescription: "Expected to decode String but found a dictionary instead.", underlyingError: nil)

1 Ответ

0 голосов
/ 07 января 2019

Вам просто нужно реализовать init(from:) самостоятельно и попытаться декодировать значение для клавиши subscription как Dictionary, представляющее Subs, так и String.

.
struct OrderLine: Codable {
    let absUrl: String?
    let restApiUrl : String?
    let description : String?
    let quantity : Int?
    let subscription: Subs?
    let total: Double?

    private enum CodingKeys: String, CodingKey {
        case absUrl, restApiUrl, description, quantity, subscription, total
    }

    init(from decoder:Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.absUrl = try container.decodeIfPresent(String.self, forKey: .absUrl)
        self.restApiUrl = try container.decodeIfPresent(String.self, forKey: .restApiUrl)
        self.description = try container.decodeIfPresent(String.self, forKey: .description)
        self.quantity = try container.decodeIfPresent(Int.self, forKey: .quantity)
        self.total = try container.decodeIfPresent(Double.self, forKey: .total)
        if (try? container.decodeIfPresent(String.self, forKey: .subscription)) == nil {
            self.subscription = try container.decodeIfPresent(Subs.self, forKey: .subscription)
        } else {
            self.subscription = nil
        }
    }
}
...