我从服务器(对于相同的结构和字段(返回了"几种"不同格式的布尔值。我知道这很荒谬,但我需要找到一种方法来干净地处理它。
所以为了反序列化它,我做了一些事情(示例程序(:
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)nn")
} else {
print("State Unknownnn")
}
} catch {
print("(error)nn")
}
}
}
现在,如果我使用 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
}
}
但肯定有更好的方法吗?有什么想法吗???
与其catch
错误,不如有条件地绑定类型
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
}
}
}
一个新的建议是使用相同的编码密钥解码多值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
另一种方法,相同的原理,只是为了好玩。这很好。
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
}
}
}