AutoCat2/Shared/ThirdParty/GenericJSON/Initialization.swift

125 lines
3.3 KiB
Swift

import Foundation
private struct InitializationError: Error {}
extension JSON {
/// Create a JSON value from anything.
///
/// Argument has to be a valid JSON structure: A `Double`, `Int`, `String`,
/// `Bool`, an `Array` of those types or a `Dictionary` of those types.
///
/// You can also pass `nil` or `NSNull`, both will be treated as `.null`.
public init(_ value: Any) throws {
switch value {
case _ as NSNull:
self = .null
case let opt as Optional<Any> where opt == nil:
self = .null
case let num as NSNumber:
if num.isBool {
self = .bool(num.boolValue)
} else {
self = .number(num.doubleValue)
}
case let str as String:
self = .string(str)
case let bool as Bool:
self = .bool(bool)
case let array as [Any]:
self = .array(try array.map(JSON.init))
case let dict as [String:Any]:
self = .object(try dict.mapValues(JSON.init))
default:
throw InitializationError()
}
}
}
extension JSON {
/// Create a JSON value from an `Encodable`. This will give you access to the raw
/// encoded JSON value the `Encodable` is serialized into.
public init<T: Encodable>(encodable: T) throws {
let encoded = try JSONEncoder().encode(encodable)
self = try JSONDecoder().decode(JSON.self, from: encoded)
}
}
extension JSON: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: Bool) {
self = .bool(value)
}
}
extension JSON: ExpressibleByNilLiteral {
public init(nilLiteral: ()) {
self = .null
}
}
extension JSON: ExpressibleByArrayLiteral {
public init(arrayLiteral elements: JSON...) {
self = .array(elements)
}
}
extension JSON: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, JSON)...) {
var object: [String:JSON] = [:]
for (k, v) in elements {
object[k] = v
}
self = .object(object)
}
}
extension JSON: ExpressibleByFloatLiteral {
public init(floatLiteral value: Double) {
self = .number(value)
}
}
extension JSON: ExpressibleByIntegerLiteral {
public init(integerLiteral value: Int) {
self = .number(Double(value))
}
}
extension JSON: ExpressibleByStringLiteral {
public init(stringLiteral value: String) {
self = .string(value)
}
}
// MARK: - NSNumber
extension NSNumber {
/// Boolean value indicating whether this `NSNumber` wraps a boolean.
///
/// For example, when using `NSJSONSerialization` Bool values are converted into `NSNumber` instances.
///
/// - seealso: https://stackoverflow.com/a/49641315/3589408
fileprivate var isBool: Bool {
let objCType = String(cString: self.objCType)
if (self.compare(trueNumber) == .orderedSame && objCType == trueObjCType) || (self.compare(falseNumber) == .orderedSame && objCType == falseObjCType) {
return true
} else {
return false
}
}
}
private let trueNumber = NSNumber(value: true)
private let falseNumber = NSNumber(value: false)
private let trueObjCType = String(cString: trueNumber.objCType)
private let falseObjCType = String(cString: falseNumber.objCType)