在Swift 4.2中,对Int和Bool值的类型检查未正确返回

时间:2018-11-29 21:07:23

标签: swift

我目前在Swift 4.2中遇到一个问题,我想请人帮忙解决。

目前,我正在反序列化JSON响应,这是一个字典,其键的类型为String,其值可以为类型IntBool。以下是一个很好的例子:

{
    "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值导致控制台打印为IntBool类型的控制台。

对于上下文,我确实在StackOverflow上找到了以下问题,但是除非我检查类型为CFBool而不是类型为Bool,否则答案是没有用的。

Is it possible to distinguish Bool and Int in Swift?

有人可以告诉我我做错了什么吗,或者这是Swift 4.2的问题吗?

我非常感谢您的帮助,感谢所有阅读此文章的人。

2 个答案:

答案 0 :(得分:3)

不幸的是,您不能使用Core Foundation类型或Objective-C @encode字符串来解决很低的问题。

问题在于,在幕后,Foundation的JSON序列化使用NSNumber来包装整数和布尔值。因此,JSON 0和JSON true都被转换为NSNumber对象,而Swift愿意将其中两个NSNumber对象转换为IntBool的请求。

但是,实际上,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进行测试的另一种方法是认识到只有两个CFBooleanCFBooleanRefkCFBooleanTrue。您可以使用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])