【问题标题】:Swift Decode a data type with many different formatsSwift 解码具有多种不同格式的数据类型
【发布时间】:2019-02-28 12:53:45
【问题描述】:

我正在从服务器返回一个“几种”不同格式的布尔值(对于相同的结构和字段)。我知道这很荒谬,但我需要找到一种方法来干净地处理它。

所以要反序列化它,我会执行类似(示例程序)的操作:

import Foundation

struct Foo: Codable {
    var isOpen: Bool?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        isOpen = try container.decodeIfPresent(Bool.self, forKey: .isOpen)
    }

    enum CodingKeys: String, CodingKey {
        case isOpen
    }
}

//He sends any one of these..
let json1 = "{ \"isOpen\": \"true\" }"
let json2 = "{ \"isOpen\": \"false\" }"
let json3 = "{ \"isOpen\": true }"
let json4 = "{ \"isOpen\": false }"
let json5 = "{ \"isOpen\": null }"
let json6 = "{ \"isOpen\": \"null\" }"
let json7 = "{ \"isOpen\": \"<null>\" }"

//He doesn't send this one.. but I wouldn't be surprised if I got it so I added it for fun (serializing the below `json8` and `json9` is not required for an answer).. :)

let json8 = "{ \"isOpen\": 0 }"
let json9 = "{ \"isOpen\": 1 }"

let json = [json1, json2, json3, json4, json5, json6, json7, json8, json9]
for js in json {
    if let rawData = js.data(using: .utf8) {
        do {
            let foo = try JSONDecoder().decode(Foo.self, from: rawData)
            if let isOpen = foo.isOpen {
                print("\(isOpen)\n\n")
            } else {
                print("State Unknown\n\n")
            }
        } catch {
            print("\(error)\n\n")
        }
    }
}

现在,如果我使用 Swift Codable(我们所有的数据结构都已经使用),那么我们将得到类型不匹配并引发错误/异常。我曾考虑尝试捕获每个案例并尝试使用不同类型的另一个解码,但最终会像:

do {
    isOpen = try container.decode(Bool.self, forKey: .isOpen)
}
catch {
    do {
        isOpen = try container.decode(Int.self, forKey: .isOpen) != 0
    }
    catch {
        do {
            isOpen = Bool(try container.decode(String.self, forKey: .isOpen))
        }
        catch {
            do {
                isOpen = try container.decodeIfPreset(Bool.self, forKey: .isOpen)  ?? GiveUpAndAssignDefaultValueHere..
            }
            catch {
                isOpen = nil //no idea..
            }
        }
    }
}

然后这让我开始考虑先将其转换为字符串,然后尝试解析它,所以我最终得到了(至少比上面的更好):

do {
    isOpen = try container.decode(Bool?.self, forKey: .isOpen)
}
catch {
    do {
        isOpen = Bool(try container.decode(String.self, forKey: .isOpen))
    }
    catch {
        isOpen = try container.decode(Int.self, forKey: .isOpen) != 0
    }
}

但肯定有更好的方法吗?有什么想法???

【问题讨论】:

  • 您想始终将最终值用作 Bool 吗?

标签: ios codable decodable swift4.2


【解决方案1】:

一个新建议是使用相同的 CodingKey isOpen 解码多值

代码是这样的,

struct Foo: Codable {
var isOpen: Bool?
private var isOpenInty: Int?
private var isOpenStringy: String?
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    do {
        isOpen = try container.decodeIfPresent(Bool.self, forKey: .isOpen)

    }catch {
        do {
            isOpenInty = try container.decodeIfPresent(Int.self, forKey: .isOpen)
            if isOpenInty == 0  {isOpen = true} else {isOpen = false}
        }catch {
            isOpenStringy = try container.decodeIfPresent(String.self, forKey: .isOpen)
            if isOpenStringy == "true" {isOpen = true} else {isOpen = false}
        }
    }
}

并根据这些值中的任何一个,设置isOpen 值。

只是处理这种情况的另一种方式。

和你的想法差不多,

   do {
        isOpen = try container.decode(Bool?.self, forKey: .isOpen)
    }
    catch {
        do {
            isOpen = Bool(try container.decode(String.self, forKey: .isOpen))
        }
        catch {
            isOpen = try container.decode(Int.self, forKey: .isOpen) != 0
        }
    }

但是你的代码在 null 的情况下给出了State Unknown,上面的代码只是在 null 的情况下处理它,作为 false

【讨论】:

  • 几乎你的代码和这个,是迄今为止处理这种情况的最干净的方法。如果您有更好的方法,请考虑这个答案,请发布它,因为我想检查一下!
【解决方案2】:

我会有条件地绑定类型而不是catching 错误

struct Foo: Codable {
    var isOpen: Bool?

    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        if let boolOpen = try? container.decode(Bool.self, forKey: .isOpen) {
            isOpen = boolOpen
        } else if let intOpen = try? container.decode(Int.self, forKey: .isOpen) {
            isOpen = intOpen == 1
        } else if let stringOpen = try? container.decode(String.self, forKey: .isOpen) {
            switch stringOpen {
            case "true", "1": isOpen = true
            case "false", "0": isOpen = false
            default : isOpen = nil
            }
        } else {
            isOpen = nil
        }
    }
}

【讨论】:

    【解决方案3】:

    另一种方法,相同的原理,只是为了好玩。没关系。

        init(from decoder: Decoder) throws {
    
    let container = try decoder.container(keyedBy: CodingKeys.self)
    _  =  [ try? container.decode(Bool.self, forKey: .isOpen),
        try?  container.decode(String.self, forKey: .isOpen),
        try? container.decode(Int.self, forKey: .isOpen)].first{
    switch $0 {
    case is Bool:
        self.isOpen  = $0 as? Bool
        return true
    case is Int:
        self.isOpen  = ($0 as! Int) == 0 ? false : (($0 as! Int) == 1 ? true : nil)
        return true
    case is String:
        self.isOpen  = Bool.init($0 as! String)
        return true
    default:
        return false
    }
        }
    }
    

    【讨论】:

    • 您可以使用以下语法消除强制转换的需要:case b as Bool
    猜你喜欢
    • 2020-04-09
    • 2022-11-28
    • 1970-01-01
    • 1970-01-01
    • 2021-01-17
    • 2023-03-19
    • 1970-01-01
    • 2019-09-23
    • 1970-01-01
    相关资源
    最近更新 更多