К сожалению, вы не можете решить эту проблему, не опустившись на довольно низкий уровень, используя либо базовые типы Foundation, либо строки Objective-C @encode
.
Проблема в том, что под прикрытием FoundationJSON-сериализация использует NSNumber
для переноса как целых, так и логических значений.Таким образом, JSON 0
и JSON true
оба преобразуются в NSNumber
объекты, и Swift желает преобразовать любой из этих NSNumber
объектов в Int
или Bool
по запросу.
Однако логические значения JSON фактически преобразуются в подкласс NSNumber
, называемый __NSCFBoolean
, к которому относится тип CFBooleanRef
(в Swift, 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))")
}
}
Вывод:
number_of_likes __NSCFNumber q
is_liked __NSCFBoolean c
Не задокументировано, что сериализация Foundation JSON декодирует логические значения JSON в Core Foundation CFBoolean
s, но вряд ли изменится.
Так что вот основной способ Foundationtest:
if let isLikedCF = jso["is_liked"] as CFTypeRef?,
CFGetTypeID(isLikedCF) == CFBooleanGetTypeID()
{
print("it's bool")
} else {
print("it's not bool")
}
Здесь мы конвертируем значение из словаря JSON в CFTypeRef
(который является ссылкой на любой базовый тип Foundation, и все, что возвращает JSONSerialization
Foundation, этобесплатное соединение с типом Core Foundation), а затем проверка того, является ли идентификатор типа объекта Core Foundation идентификатором типа CFBoolean
.
Другой способ проверки с использованием Core Foundation состоит в том, чтобы распознать, что существует только дваo 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
, запрашиваете objCType
, конвертируете полученную C-строку в Swift 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
происходит от @encode(BOOL)
(в Objective-C), где BOOL
- это typedef, равный signed char
.Это действительно неясные вещи.Я бы рекомендовал пройти базовый тест (показанный выше), так как его легче понять и лучше документировать.