Как расшифровать одно необычное свойство среди множества Decodable Swift?

У меня есть структура, соответствующая Decodable. Он имеет 50 свойств String и только один Bool. Этот bool поступает с сервера как строка false / true или иногда как целое число 0/1, поэтому не может быть декодирован из коробки. Как я могу сделать это декодирование, но не писать огромное количество ручного декодирования всех этих 50 свойств String? Может быть, как-то переопределить decodeIfPresent для Bool, но я не мог заставить его работать. Как я могу избежать создания init с декодированием всего и всего этого вручную и обрабатывать только один Bool? Если возможно, без вычисляемого свойства.

struct Response: Decodable {
    var s1: String
    var s2: String
    var s3: String
    //...........
    var s50: String
    var b1: Bool
    var b2: Bool
}

Вот пример json:

{
    "s1":"string"
    "s2":"string"
    "s3":"string"
    //..........
    "s50":"string"
    "b1":"true"
    "b2":"0"
}

Пробовал, но не работает (((

extension KeyedDecodingContainer { //Doesn't work, no execution
    func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool {
        return try! self.decodeIfPresent(Bool.self, forKey: key)
    }
    func decodeIfPresent(_ type: Bool.Type, forKey key: K) throws -> Bool? {
        return try? self.decodeIfPresent(Bool.self, forKey: key)
    }
}

person bodich    schedule 31.01.2021    source источник


Ответы (1)


Один из способов решить эту проблему - написать оболочку свойств, которая обрабатывает настраиваемое декодирование, например:

@propertyWrapper
struct FunnyBool: Decodable {
    var wrappedValue: Bool

    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        if let value = try? container.decode(Bool.self) {
            wrappedValue = value
        }
        else if
            let string = try? container.decode(String.self),
            let value = Bool(string)
        {
            wrappedValue = value
        }
        else if
            let int = try? container.decode(Int.self),
            let value = int == 0 ? false : int == 1 ? true : nil
        {
            wrappedValue = value
        }
        else {
            // Default...
            wrappedValue = false
        }
    }
}

Используйте это так:

struct Response: Decodable {
    var s1: String
    var s2: String
    @FunnyBool var b: Bool
}

let json = #"{ "s1": "hello", "s2": "world", "b": 1 }"#
let response = try! JSONDecoder().decode(Response.self, from: json.data(using: .utf8)!)
dump(response)

Выход на площадку:

▿ __lldb_expr_11.Response
  - s1: "hello"
  - s2: "world"
  ▿ _b: __lldb_expr_11.FunnyBool
    - wrappedValue: true
person rob mayoff    schedule 31.01.2021