我目前在Swift 4.2中遇到一个问题,我想请人帮忙解决。
目前,我正在反序列化JSON响应,这是一个字典,其键的类型为String
,其值可以为类型Int
或Bool
。以下是一个很好的例子:
{
"number_of_likes": 0,
"is_liked": true
}
反序列化对象时,JSON响应的类型为[String: Any]
,这是预期的。
任务:
我需要创建一个数组,详细说明哪些键的类型为Bool
并设置为true
。
问题:
使用上面突出显示的响应运行以下代码时:
guard let json = json as? [String: Any] else {
return
}
for key in dict.keys {
print("KEY: \(key)")
let value = dict[key]
if value is Int {
print("It is an integer")
}
if value is Bool {
print("It is a bool")
}
}
返回字符串
控制台将打印以下内容:
t+20.765 KEY: number_of_likes
t+20.765 It is an integer
t+20.765 It is a bool
t+20.765 KEY: is_liked
t+20.765 It is an integer
t+20.765 It is a bool
如您所见, 0 Int
值使控制台打印出它是Bool和Int类型,而 true Bool值导致控制台打印为Int
和Bool
类型的控制台。
对于上下文,我确实在StackOverflow上找到了以下问题,但是除非我检查类型为CFBool
而不是类型为Bool
,否则答案是没有用的。
Is it possible to distinguish Bool and Int in Swift?
有人可以告诉我我做错了什么吗,或者这是Swift 4.2的问题吗?
我非常感谢您的帮助,感谢所有阅读此文章的人。
答案 0 :(得分:3)
不幸的是,您不能使用Core Foundation类型或Objective-C @encode
字符串来解决很低的问题。
问题在于,在幕后,Foundation的JSON序列化使用NSNumber
来包装整数和布尔值。因此,JSON 0
和JSON true
都被转换为NSNumber
对象,而Swift愿意将其中两个NSNumber
对象转换为Int
或Bool
的请求。
但是,实际上,JSON布尔值已转换为名为NSNumber
的{{1}}的子类,这是__NSCFBoolean
(在Swift中,CFBooleanRef
)引用的类型:
CFBoolean
输出:
import Foundation
let json = """
{
"number_of_likes": 0,
"is_liked": true
}
"""
let data = json.data(using: .utf8)!
let jso = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: Any]
for key in jso.keys {
if let value = jso[key] as? NSNumber {
print("\(key) \(type(of: value)) \(String(cString: value.objCType))")
}
}
Foundation JSON序列化不会将JSON布尔值解码为Core Foundation number_of_likes __NSCFNumber q
is_liked __NSCFBoolean c
,但没有记录。
因此,这是Core Foundation的测试方法:
CFBoolean
我们在这里所做的就是将JSON字典中的值转换为if let isLikedCF = jso["is_liked"] as CFTypeRef?,
CFGetTypeID(isLikedCF) == CFBooleanGetTypeID()
{
print("it's bool")
} else {
print("it's not bool")
}
(这是对任何Core Foundation类型的引用,并且Foundation CFTypeRef
返回的所有内容都是免费桥接的)设置为Core Foundation类型),然后检查Core Foundation对象的类型ID是否为JSONSerialization
类型ID。
使用Core Foundation进行测试的另一种方法是认识到只有两个CFBoolean
值CFBooleanRef
和kCFBooleanTrue
。您可以使用kCFBooleanFalse
查看jso["is_liked"] as? NSNumber
是否与这两个值之一相同:
===
您还可以通过检查if let isLikedNumber = jso["is_liked"] as? NSNumber,
isLikedNumber === kCFBooleanTrue || isLikedNumber === kCFBooleanFalse
{
print("it's bool")
} else {
print("it's not bool")
}
的Objective-C类型代码进行测试。您转换NSNumber
,要求其jso["is_liked"] as? NSNumber
,将所得的C字符串转换为Swift objCType
,然后将其与String
进行比较。如果是这样,则为布尔值。否则,不是。
"c"
if let isLikedNumber = jso["is_liked"] as? NSNumber {
if String(cString: isLikedNumber.objCType) == "c" {
print("it's bool")
} else {
print("it's not bool")
}
}
来自c
(在Objective-C中),其中@encode(BOOL)
是BOOL
的typedef。这真的是晦涩的东西。我建议您进行Core Foundation测试(如上所示),因为它更易于理解和记录。
答案 1 :(得分:1)
您应该执行类似的操作
import UIKit
var str = """
{
"number_of_likes": 0,
"is_liked": true
}
"""
struct JsonStruct: Decodable {
var boolWithKey: [String: Bool]?
var intWithKey: [String: Int]?
init(from decoder: Decoder) {
guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
fatalError()
}
for key in container.allKeys {
if let possibleInt = try? container.decode(Int.self, forKey: key) {
intWithKey = [key.stringValue: possibleInt]
}
if let possibleBool = try? container.decode(Bool.self, forKey: key) {
boolWithKey = [key.stringValue: possibleBool]
}
}
print(container.allKeys)
}
struct CodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
return nil
}
}
}
let jsonData = str.data(using: .utf8)!
let jsonDecoder = JSONDecoder()
let jsonStruct = try! jsonDecoder.decode(JsonStruct.self, from: jsonData)
print("\(jsonStruct.boolWithKey)")
print("\(jsonStruct.intWithKey)")
输出:
CodingKeys(stringValue:“ number_of_likes”,intValue:nil),
CodingKeys(stringValue:“ is_liked”,intValue:nil)]
可选项([[“ is_liked”:true])
可选([[“ number_of_likes”:0])