使用JSONSerialization()动态计算布尔值

时间:2018-04-04 00:52:47

标签: ios swift swift4.1

我从服务器(或文件)获取JSON字符串。

我想解析那个JSON字符串,动态找出每个值类型。

但是,当涉及布尔值时,JSONSerialization只是将值转换为01,代码无法区分是否" 0&# 34;是DoubleIntBool

我想在没有明确知道特定键对应Bool值的情况下识别该值是否为Bool。我做错了什么,或者我能做些什么不同?

// What currently is happening:
let jsonString = "{\"boolean_key\" : true}"
let jsonData = jsonString.data(using: .utf8)!
let json = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String:Any]

json["boolean_key"] is Double // true
json["boolean_key"] is Int // true
json["boolean_key"] is Bool // true

// What I would like to happen is below (the issue doesn't happen if I don't use JSONSerialization):
let customJson: [String:Any] = [
    "boolean_key" : true
]

customJson["boolean_key"] is Double // false
customJson["boolean_key"] is Int // false
customJson["boolean_key"] is Bool // true

相关:

3 个答案:

答案 0 :(得分:3)

这种混乱是Swift< - > Objective-C桥内置的所有奇妙魔法的“特征”的结果。具体来说,isas关键字的行为不符合您的预期,因为JSONSerialization对象实际上是用Objective-C编写的,而不是将这些数字存储为Swift Int s,DoubleBool s,而是NSNumber个对象,而网桥只是神奇地让isas转换NSNumber到任何可以转换为它们的Swift数字类型。这就是is为每个true类型提供NSNumber的原因。

幸运的是,我们可以通过将数值转换为NSNumber来解决这个问题,从而避免桥接。从那里开始,我们遇到了更多过渡性的恶作剧,因为NSNumber免费为布尔人CFBoolean和大多数其他事情CFNumber。因此,如果我们跳过所有环节以达到CF水平,我们可以做以下事情:

if let num = json["boolean_key"] as? NSNumber {
    switch CFGetTypeID(num as CFTypeRef) {
        case CFBooleanGetTypeID():
            print("Boolean")
        case CFNumberGetTypeID():
            switch CFNumberGetType(num as CFNumber) {
            case .sInt8Type:
                print("Int8")
            case .sInt16Type:
                print("Int16")
            case .sInt32Type:
                print("Int32")
            case .sInt64Type:
                print("Int64")
            case .doubleType:
                print("Double")
            default:
                print("some other num type")
            }
        default:
            print("Something else")
    }
}

答案 1 :(得分:3)

由于JSONSerialization会将每个值转换为NSNumber,因此可以通过尝试找出每个NSNumber实例的内容来实现:https://stackoverflow.com/a/30223989/826435

let jsonString = "{ \"boolean_key\" : true, \"integer_key\" : 1 }"
let jsonData = jsonString.data(using: .utf8)!
let json = try! JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String:Any]

extension NSNumber {
    var isBool: Bool {
        return type(of: self) == type(of: NSNumber(booleanLiteral: true))
    }
}

(json["boolean_key"] as! NSNumber).isBool // true
(json["integer_key"] as! NSNumber).isBool // false

(注意:我已经输入了类似的[更好]答案,但我想留下我的答案给其他人看不同的方法)

答案 2 :(得分:2)

当您使用JSONSerialization时,任何Bool值(truefalse)都会转换为NSNumber个实例,这就是使用is Double,{的原因{1}}和is Int都返回true,因为is Bool可以转换为所有类型。

您还可以获得JSON中实际数字的NSNumber实例。

但好消息是,实际上,你实际上得到了NSNumber的特殊内部子类。布尔值实际上为您提供NSNumber,而实际数字为您提供__NSCFBoolean。当然,您实际上并不想检查这些内部类型。

这是一个更全面的例子,显示上述以及一个可行的解决方案,以检查实际的布尔值与"正常"号。

__NSCFNumber