【问题标题】:Using JSONSerialization() to dynamically figure out boolean values使用 JSONSerialization() 动态计算布尔值
【发布时间】:2018-09-13 10:20:28
【问题描述】:

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

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

但是,对于布尔值,JSONSerialization 只是将值转换为01,代码无法区分“0”是DoubleInt 还是Bool

我想在不明确知道特定键对应于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

相关:

【问题讨论】:

    标签: ios swift swift4.1


    【解决方案1】:

    这种混乱是 SwiftObjective-C 桥中内置的所有奇妙魔法的“特性”的结果。具体来说,isas 关键字的行为与您期望的不同,因为实际上是用 Objective-C 编写的 JSONSerialization 对象将这些数字存储为 Swift Ints, @ 987654325@s 或 Bools,而是作为 NSNumber 对象,并且桥神奇地使 isasNSNumbers 转换为可以转换为的任何 Swift 数字类型。这就是为什么is 为每个NSNumber 类型提供true

    幸运的是,我们可以通过将数值转换为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")
        }
    }
    

    【讨论】:

    • 非常感谢,我开始考虑类似的路线,因为在对字典进行漂亮打印时,它能够打印出它们是布尔值的事实
    【解决方案2】:

    当您使用 JSONSerialization 时,任何 Bool 值(truefalse)都会转换为 NSNumber 实例,这就是为什么使用 is Doubleis Intis Bool 都返回 true因为NSNumber 可以转换为所有这些类型。

    您还将获得一个 NSNumber 实例,用于 JSON 中的实际数字。

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

    这是一个更完整的示例,显示了上述内容以及检查实际布尔值与“正常”数字的可行解决方案。

    let jsonString = "{\"boolean_key\" : true, \"int_key\" : 1}"
    let jsonData = jsonString.data(using: .utf8)!
    let json = try! JSONSerialization.jsonObject(with: jsonData, options: []) as! [String:Any]
    
    print(type(of: json["boolean_key"]!)) // __NSCFBoolean
    json["boolean_key"] is Double // true
    json["boolean_key"] is Int // true
    json["boolean_key"] is Bool // true
    
    print(type(of: json["int_key"]!)) // __NSCFNumber
    json["int_key"] is Double // true
    json["int_key"] is Int // true
    json["int_key"] is Bool // true
    
    print(type(of: json["boolean_key"]!) == type(of: NSNumber(value: true))) // true
    print(type(of: json["boolean_key"]!) == type(of: NSNumber(value: 1))) // false
    print(type(of: json["int_key"]!) == type(of: NSNumber(value: 0))) // true
    print(type(of: json["int_key"]!) == type(of: NSNumber(value: true))) // false
    

    【讨论】:

    • 如果您转到公开的 CF 级别,您可以实际检查这些内部类型;看我的回答。
    • @CharlesSrstka 我没有说你不能检查内部类型。我建议您不应该这样做,因为它们是内部的。你的支票没问题。我的意思是不检查__NSCFBoolean
    • __NSCFBoolean 只是CFBoolean 的免费桥,我们可以很容易地检查它。
    • 非常感谢,我开始考虑类似的路线,因为在对字典进行漂亮打印时,它能够打印出它们是布尔值的事实
    • 我喜欢你没有“硬编码”实际类型的事实,所以这似乎是最安全的路线
    【解决方案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
    

    (注意:我在打字时已经得到了类似的 [更好] 答案,但我想把我的答案留给其他正在寻找不同方法的人)

    【讨论】:

      猜你喜欢
      • 2017-07-30
      • 1970-01-01
      • 1970-01-01
      • 2012-08-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-08
      • 1970-01-01
      相关资源
      最近更新 更多