8

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

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

但是,当涉及到布尔值时,JSONSerialization只需将值转换为0or 1,代码无法区分“0”是 a DoubleInt还是Bool

Bool我想在不明确知道特定键对应于值的情况下识别该值是否为a 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

有关的:

4

3 回答 3

9

这种混乱是 Swift<->Objective-C 桥中内置的所有奇妙魔法的“功能”的结果。具体来说,isandas关键字的行为与您期望的不同,因为JSONSerialization实际上是用 Objective-C 编写的对象将这些数字存储为 Swift Ints、Doubles 或Bools,而是作为NSNumber对象和桥只是神奇地制作is并将sas转换NSNumber为可以转换为的任何 Swift 数字类型。所以这就是为什么is给你true每种NSNumber类型。

幸运的是,我们可以通过将数值转换为NSNumber来解决这个问题,从而避免了桥。从那里开始,我们遇到了更多的桥接恶作剧,因为对于布尔值和大多数其他事情来说,NSNumber它是免费桥接的。因此,如果我们跳过所有的障碍以降低到 CF 级别,我们可以执行以下操作:CFBooleanCFNumber

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")
    }
}
于 2018-04-04T01:23:36.910 回答
6

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

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

但好消息是,实际上,您实际上获得了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
于 2018-04-04T01:24:52.417 回答
5

因为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

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

于 2018-04-04T01:32:58.737 回答