AutorÃa | Ultima modificación | Ver Log |
// SwiftyJSON.swift//// Copyright (c) 2014 - 2017 Ruoyu Fu, Pinglin Tang//// Permission is hereby granted, free of charge, to any person obtaining a copy// of this software and associated documentation files (the "Software"), to deal// in the Software without restriction, including without limitation the rights// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell// copies of the Software, and to permit persons to whom the Software is// furnished to do so, subject to the following conditions://// The above copyright notice and this permission notice shall be included in// all copies or substantial portions of the Software.//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN// THE SOFTWARE.import Foundation// MARK: - Error// swiftlint:disable line_lengthpublic enum SwiftyJSONError: Int, Swift.Error {case unsupportedType = 999case indexOutOfBounds = 900case elementTooDeep = 902case wrongType = 901case notExist = 500case invalidJSON = 490}extension SwiftyJSONError: CustomNSError {/// return the error domain of SwiftyJSONErrorpublic static var errorDomain: String { return "com.swiftyjson.SwiftyJSON" }/// return the error code of SwiftyJSONErrorpublic var errorCode: Int { return self.rawValue }/// return the userInfo of SwiftyJSONErrorpublic var errorUserInfo: [String: Any] {switch self {case .unsupportedType:return [NSLocalizedDescriptionKey: "It is an unsupported type."]case .indexOutOfBounds:return [NSLocalizedDescriptionKey: "Array Index is out of bounds."]case .wrongType:return [NSLocalizedDescriptionKey: "Couldn't merge, because the JSONs differ in type on top level."]case .notExist:return [NSLocalizedDescriptionKey: "Dictionary key does not exist."]case .invalidJSON:return [NSLocalizedDescriptionKey: "JSON is invalid."]case .elementTooDeep:return [NSLocalizedDescriptionKey: "Element too deep. Increase maxObjectDepth and make sure there is no reference loop."]}}}// MARK: - JSON Type/**JSON's type definitions.See http://www.json.org*/public enum Type: Int {case numbercase stringcase boolcase arraycase dictionarycase nullcase unknown}// MARK: - JSON Basepublic struct JSON {/**Creates a JSON using the data.- parameter data: The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary- parameter opt: The JSON serialization reading options. `[]` by default.- returns: The created JSON*/public init(data: Data, options opt: JSONSerialization.ReadingOptions = []) throws {let object: Any = try JSONSerialization.jsonObject(with: data, options: opt)self.init(jsonObject: object)}/**Creates a JSON object- note: this does not parse a `String` into JSON, instead use `init(parseJSON: String)`- parameter object: the object- returns: the created JSON object*/public init(_ object: Any) {switch object {case let object as Data:do {try self.init(data: object)} catch {self.init(jsonObject: NSNull())}default:self.init(jsonObject: object)}}/**Parses the JSON string into a JSON object- parameter json: the JSON string- returns: the created JSON object*/public init(parseJSON jsonString: String) {if let data = jsonString.data(using: .utf8) {self.init(data)} else {self.init(NSNull())}}/**Creates a JSON using the object.- parameter jsonObject: The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity.- returns: The created JSON*/fileprivate init(jsonObject: Any) {object = jsonObject}/**Merges another JSON into this JSON, whereas primitive values which are not present in this JSON are getting added,present values getting overwritten, array values getting appended and nested JSONs getting merged the same way.- parameter other: The JSON which gets merged into this JSON- throws `ErrorWrongType` if the other JSONs differs in type on the top level.*/public mutating func merge(with other: JSON) throws {try self.merge(with: other, typecheck: true)}/**Merges another JSON into this JSON and returns a new JSON, whereas primitive values which are not present in this JSON are getting added,present values getting overwritten, array values getting appended and nested JSONS getting merged the same way.- parameter other: The JSON which gets merged into this JSON- throws `ErrorWrongType` if the other JSONs differs in type on the top level.- returns: New merged JSON*/public func merged(with other: JSON) throws -> JSON {var merged = selftry merged.merge(with: other, typecheck: true)return merged}/**Private woker function which does the actual mergingTypecheck is set to true for the first recursion level to prevent total override of the source JSON*/fileprivate mutating func merge(with other: JSON, typecheck: Bool) throws {if type == other.type {switch type {case .dictionary:for (key, _) in other {try self[key].merge(with: other[key], typecheck: false)}case .array:self = JSON(arrayValue + other.arrayValue)default:self = other}} else {if typecheck {throw SwiftyJSONError.wrongType} else {self = other}}}/// Private objectfileprivate var rawArray: [Any] = []fileprivate var rawDictionary: [String: Any] = [:]fileprivate var rawString: String = ""fileprivate var rawNumber: NSNumber = 0fileprivate var rawNull: NSNull = NSNull()fileprivate var rawBool: Bool = false/// JSON type, fileprivate setterpublic fileprivate(set) var type: Type = .null/// Error in JSON, fileprivate setterpublic fileprivate(set) var error: SwiftyJSONError?/// Object in JSONpublic var object: Any {get {switch type {case .array: return rawArraycase .dictionary: return rawDictionarycase .string: return rawStringcase .number: return rawNumbercase .bool: return rawBooldefault: return rawNull}}set {error = nilswitch unwrap(newValue) {case let number as NSNumber:if number.isBool {type = .boolrawBool = number.boolValue} else {type = .numberrawNumber = number}case let string as String:type = .stringrawString = stringcase _ as NSNull:type = .nullcase nil:type = .nullcase let array as [Any]:type = .arrayrawArray = arraycase let dictionary as [String: Any]:type = .dictionaryrawDictionary = dictionarydefault:type = .unknownerror = SwiftyJSONError.unsupportedType}}}/// The static null JSON@available(*, unavailable, renamed:"null")public static var nullJSON: JSON { return null }public static var null: JSON { return JSON(NSNull()) }}/// Private method to unwarp an object recursivelyprivate func unwrap(_ object: Any) -> Any {switch object {case let json as JSON:return unwrap(json.object)case let array as [Any]:return array.map(unwrap)case let dictionary as [String: Any]:var d = dictionarydictionary.forEach { pair ind[pair.key] = unwrap(pair.value)}return ddefault:return object}}public enum Index<T: Any>: Comparable {case array(Int)case dictionary(DictionaryIndex<String, T>)case nullstatic public func == (lhs: Index, rhs: Index) -> Bool {switch (lhs, rhs) {case (.array(let left), .array(let right)): return left == rightcase (.dictionary(let left), .dictionary(let right)): return left == rightcase (.null, .null): return truedefault: return false}}static public func < (lhs: Index, rhs: Index) -> Bool {switch (lhs, rhs) {case (.array(let left), .array(let right)): return left < rightcase (.dictionary(let left), .dictionary(let right)): return left < rightdefault: return false}}}public typealias JSONIndex = Index<JSON>public typealias JSONRawIndex = Index<Any>extension JSON: Swift.Collection {public typealias Index = JSONRawIndexpublic var startIndex: Index {switch type {case .array: return .array(rawArray.startIndex)case .dictionary: return .dictionary(rawDictionary.startIndex)default: return .null}}public var endIndex: Index {switch type {case .array: return .array(rawArray.endIndex)case .dictionary: return .dictionary(rawDictionary.endIndex)default: return .null}}public func index(after i: Index) -> Index {switch i {case .array(let idx): return .array(rawArray.index(after: idx))case .dictionary(let idx): return .dictionary(rawDictionary.index(after: idx))default: return .null}}public subscript (position: Index) -> (String, JSON) {switch position {case .array(let idx): return (String(idx), JSON(rawArray[idx]))case .dictionary(let idx): return (rawDictionary[idx].key, JSON(rawDictionary[idx].value))default: return ("", JSON.null)}}}// MARK: - Subscript/*** To mark both String and Int can be used in subscript.*/public enum JSONKey {case index(Int)case key(String)}public protocol JSONSubscriptType {var jsonKey: JSONKey { get }}extension Int: JSONSubscriptType {public var jsonKey: JSONKey {return JSONKey.index(self)}}extension String: JSONSubscriptType {public var jsonKey: JSONKey {return JSONKey.key(self)}}extension JSON {/// If `type` is `.array`, return json whose object is `array[index]`, otherwise return null json with error.fileprivate subscript(index index: Int) -> JSON {get {if type != .array {var r = JSON.nullr.error = self.error ?? SwiftyJSONError.wrongTypereturn r} else if rawArray.indices.contains(index) {return JSON(rawArray[index])} else {var r = JSON.nullr.error = SwiftyJSONError.indexOutOfBoundsreturn r}}set {if type == .array &&rawArray.indices.contains(index) &&newValue.error == nil {rawArray[index] = newValue.object}}}/// If `type` is `.dictionary`, return json whose object is `dictionary[key]` , otherwise return null json with error.fileprivate subscript(key key: String) -> JSON {get {var r = JSON.nullif type == .dictionary {if let o = rawDictionary[key] {r = JSON(o)} else {r.error = SwiftyJSONError.notExist}} else {r.error = self.error ?? SwiftyJSONError.wrongType}return r}set {if type == .dictionary && newValue.error == nil {rawDictionary[key] = newValue.object}}}/// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`, return `subscript(key:)`.fileprivate subscript(sub sub: JSONSubscriptType) -> JSON {get {switch sub.jsonKey {case .index(let index): return self[index: index]case .key(let key): return self[key: key]}}set {switch sub.jsonKey {case .index(let index): self[index: index] = newValuecase .key(let key): self[key: key] = newValue}}}/**Find a json in the complex data structures by using array of Int and/or String as path.Example:```let json = JSON[data]let path = [9,"list","person","name"]let name = json[path]```The same as: let name = json[9]["list"]["person"]["name"]- parameter path: The target json's path.- returns: Return a json found by the path or a null json with error*/public subscript(path: [JSONSubscriptType]) -> JSON {get {return path.reduce(self) { $0[sub: $1] }}set {switch path.count {case 0: returncase 1: self[sub:path[0]].object = newValue.objectdefault:var aPath = pathaPath.remove(at: 0)var nextJSON = self[sub: path[0]]nextJSON[aPath] = newValueself[sub: path[0]] = nextJSON}}}/**Find a json in the complex data structures by using array of Int and/or String as path.- parameter path: The target json's path. Example:let name = json[9,"list","person","name"]The same as: let name = json[9]["list"]["person"]["name"]- returns: Return a json found by the path or a null json with error*/public subscript(path: JSONSubscriptType...) -> JSON {get {return self[path]}set {self[path] = newValue}}}// MARK: - LiteralConvertibleextension JSON: Swift.ExpressibleByStringLiteral {public init(stringLiteral value: StringLiteralType) {self.init(value)}public init(extendedGraphemeClusterLiteral value: StringLiteralType) {self.init(value)}public init(unicodeScalarLiteral value: StringLiteralType) {self.init(value)}}extension JSON: Swift.ExpressibleByIntegerLiteral {public init(integerLiteral value: IntegerLiteralType) {self.init(value)}}extension JSON: Swift.ExpressibleByBooleanLiteral {public init(booleanLiteral value: BooleanLiteralType) {self.init(value)}}extension JSON: Swift.ExpressibleByFloatLiteral {public init(floatLiteral value: FloatLiteralType) {self.init(value)}}extension JSON: Swift.ExpressibleByDictionaryLiteral {public init(dictionaryLiteral elements: (String, Any)...) {let dictionary = elements.reduce(into: [String: Any](), { $0[$1.0] = $1.1})self.init(dictionary)}}extension JSON: Swift.ExpressibleByArrayLiteral {public init(arrayLiteral elements: Any...) {self.init(elements)}}// MARK: - Rawextension JSON: Swift.RawRepresentable {public init?(rawValue: Any) {if JSON(rawValue).type == .unknown {return nil} else {self.init(rawValue)}}public var rawValue: Any {return object}public func rawData(options opt: JSONSerialization.WritingOptions = JSONSerialization.WritingOptions(rawValue: 0)) throws -> Data {guard JSONSerialization.isValidJSONObject(object) else {throw SwiftyJSONError.invalidJSON}return try JSONSerialization.data(withJSONObject: object, options: opt)}public func rawString(_ encoding: String.Encoding = .utf8, options opt: JSONSerialization.WritingOptions = .prettyPrinted) -> String? {do {return try _rawString(encoding, options: [.jsonSerialization: opt])} catch {print("Could not serialize object to JSON because:", error.localizedDescription)return nil}}public func rawString(_ options: [writingOptionsKeys: Any]) -> String? {let encoding = options[.encoding] as? String.Encoding ?? String.Encoding.utf8let maxObjectDepth = options[.maxObjextDepth] as? Int ?? 10do {return try _rawString(encoding, options: options, maxObjectDepth: maxObjectDepth)} catch {print("Could not serialize object to JSON because:", error.localizedDescription)return nil}}fileprivate func _rawString(_ encoding: String.Encoding = .utf8, options: [writingOptionsKeys: Any], maxObjectDepth: Int = 10) throws -> String? {guard maxObjectDepth > 0 else { throw SwiftyJSONError.invalidJSON }switch type {case .dictionary:do {if !(options[.castNilToNSNull] as? Bool ?? false) {let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrintedlet data = try rawData(options: jsonOption)return String(data: data, encoding: encoding)}guard let dict = object as? [String: Any?] else {return nil}let body = try dict.keys.map { key throws -> String inguard let value = dict[key] else {return "\"\(key)\": null"}guard let unwrappedValue = value else {return "\"\(key)\": null"}let nestedValue = JSON(unwrappedValue)guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else {throw SwiftyJSONError.elementTooDeep}if nestedValue.type == .string {return "\"\(key)\": \"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\""} else {return "\"\(key)\": \(nestedString)"}}return "{\(body.joined(separator: ","))}"} catch _ {return nil}case .array:do {if !(options[.castNilToNSNull] as? Bool ?? false) {let jsonOption = options[.jsonSerialization] as? JSONSerialization.WritingOptions ?? JSONSerialization.WritingOptions.prettyPrintedlet data = try rawData(options: jsonOption)return String(data: data, encoding: encoding)}guard let array = object as? [Any?] else {return nil}let body = try array.map { value throws -> String inguard let unwrappedValue = value else {return "null"}let nestedValue = JSON(unwrappedValue)guard let nestedString = try nestedValue._rawString(encoding, options: options, maxObjectDepth: maxObjectDepth - 1) else {throw SwiftyJSONError.invalidJSON}if nestedValue.type == .string {return "\"\(nestedString.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\""} else {return nestedString}}return "[\(body.joined(separator: ","))]"} catch _ {return nil}case .string: return rawStringcase .number: return rawNumber.stringValuecase .bool: return rawBool.descriptioncase .null: return "null"default: return nil}}}// MARK: - Printable, DebugPrintableextension JSON: Swift.CustomStringConvertible, Swift.CustomDebugStringConvertible {public var description: String {return rawString(options: .prettyPrinted) ?? "unknown"}public var debugDescription: String {return description}}// MARK: - Arrayextension JSON {//Optional [JSON]public var array: [JSON]? {return type == .array ? rawArray.map { JSON($0) } : nil}//Non-optional [JSON]public var arrayValue: [JSON] {return self.array ?? []}//Optional [Any]public var arrayObject: [Any]? {get {switch type {case .array: return rawArraydefault: return nil}}set {self.object = newValue ?? NSNull()}}}// MARK: - Dictionaryextension JSON {//Optional [String : JSON]public var dictionary: [String: JSON]? {if type == .dictionary {var d = [String: JSON](minimumCapacity: rawDictionary.count)rawDictionary.forEach { pair ind[pair.key] = JSON(pair.value)}return d} else {return nil}}//Non-optional [String : JSON]public var dictionaryValue: [String: JSON] {return dictionary ?? [:]}//Optional [String : Any]public var dictionaryObject: [String: Any]? {get {switch type {case .dictionary: return rawDictionarydefault: return nil}}set {object = newValue ?? NSNull()}}}// MARK: - Boolextension JSON { // : Swift.Bool//Optional boolpublic var bool: Bool? {get {switch type {case .bool: return rawBooldefault: return nil}}set {object = newValue ?? NSNull()}}//Non-optional boolpublic var boolValue: Bool {get {switch type {case .bool: return rawBoolcase .number: return rawNumber.boolValuecase .string: return ["true", "y", "t", "yes", "1"].contains { rawString.caseInsensitiveCompare($0) == .orderedSame }default: return false}}set {object = newValue}}}// MARK: - Stringextension JSON {//Optional stringpublic var string: String? {get {switch type {case .string: return object as? Stringdefault: return nil}}set {object = newValue ?? NSNull()}}//Non-optional stringpublic var stringValue: String {get {switch type {case .string: return object as? String ?? ""case .number: return rawNumber.stringValuecase .bool: return (object as? Bool).map { String($0) } ?? ""default: return ""}}set {object = newValue}}}// MARK: - Numberextension JSON {//Optional numberpublic var number: NSNumber? {get {switch type {case .number: return rawNumbercase .bool: return NSNumber(value: rawBool ? 1 : 0)default: return nil}}set {object = newValue ?? NSNull()}}//Non-optional numberpublic var numberValue: NSNumber {get {switch type {case .string:let decimal = NSDecimalNumber(string: object as? String)return decimal == .notANumber ? .zero : decimalcase .number: return object as? NSNumber ?? NSNumber(value: 0)case .bool: return NSNumber(value: rawBool ? 1 : 0)default: return NSNumber(value: 0.0)}}set {object = newValue}}}// MARK: - Nullextension JSON {public var null: NSNull? {set {object = NSNull()}get {switch type {case .null: return rawNulldefault: return nil}}}public func exists() -> Bool {if let errorValue = error, (400...1000).contains(errorValue.errorCode) {return false}return true}}// MARK: - URLextension JSON {//Optional URLpublic var url: URL? {get {switch type {case .string:// Check for existing percent escapes first to prevent double-escaping of % characterif rawString.range(of: "%[0-9A-Fa-f]{2}", options: .regularExpression, range: nil, locale: nil) != nil {return Foundation.URL(string: rawString)} else if let encodedString_ = rawString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {// We have to use `Foundation.URL` otherwise it conflicts with the variable name.return Foundation.URL(string: encodedString_)} else {return nil}default:return nil}}set {object = newValue?.absoluteString ?? NSNull()}}}// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64extension JSON {public var double: Double? {get {return number?.doubleValue}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var doubleValue: Double {get {return numberValue.doubleValue}set {object = NSNumber(value: newValue)}}public var float: Float? {get {return number?.floatValue}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var floatValue: Float {get {return numberValue.floatValue}set {object = NSNumber(value: newValue)}}public var int: Int? {get {return number?.intValue}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var intValue: Int {get {return numberValue.intValue}set {object = NSNumber(value: newValue)}}public var uInt: UInt? {get {return number?.uintValue}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var uIntValue: UInt {get {return numberValue.uintValue}set {object = NSNumber(value: newValue)}}public var int8: Int8? {get {return number?.int8Value}set {if let newValue = newValue {object = NSNumber(value: Int(newValue))} else {object = NSNull()}}}public var int8Value: Int8 {get {return numberValue.int8Value}set {object = NSNumber(value: Int(newValue))}}public var uInt8: UInt8? {get {return number?.uint8Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var uInt8Value: UInt8 {get {return numberValue.uint8Value}set {object = NSNumber(value: newValue)}}public var int16: Int16? {get {return number?.int16Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var int16Value: Int16 {get {return numberValue.int16Value}set {object = NSNumber(value: newValue)}}public var uInt16: UInt16? {get {return number?.uint16Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var uInt16Value: UInt16 {get {return numberValue.uint16Value}set {object = NSNumber(value: newValue)}}public var int32: Int32? {get {return number?.int32Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var int32Value: Int32 {get {return numberValue.int32Value}set {object = NSNumber(value: newValue)}}public var uInt32: UInt32? {get {return number?.uint32Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var uInt32Value: UInt32 {get {return numberValue.uint32Value}set {object = NSNumber(value: newValue)}}public var int64: Int64? {get {return number?.int64Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var int64Value: Int64 {get {return numberValue.int64Value}set {object = NSNumber(value: newValue)}}public var uInt64: UInt64? {get {return number?.uint64Value}set {if let newValue = newValue {object = NSNumber(value: newValue)} else {object = NSNull()}}}public var uInt64Value: UInt64 {get {return numberValue.uint64Value}set {object = NSNumber(value: newValue)}}}// MARK: - Comparableextension JSON: Swift.Comparable {}public func == (lhs: JSON, rhs: JSON) -> Bool {switch (lhs.type, rhs.type) {case (.number, .number): return lhs.rawNumber == rhs.rawNumbercase (.string, .string): return lhs.rawString == rhs.rawStringcase (.bool, .bool): return lhs.rawBool == rhs.rawBoolcase (.array, .array): return lhs.rawArray as NSArray == rhs.rawArray as NSArraycase (.dictionary, .dictionary): return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionarycase (.null, .null): return truedefault: return false}}public func <= (lhs: JSON, rhs: JSON) -> Bool {switch (lhs.type, rhs.type) {case (.number, .number): return lhs.rawNumber <= rhs.rawNumbercase (.string, .string): return lhs.rawString <= rhs.rawStringcase (.bool, .bool): return lhs.rawBool == rhs.rawBoolcase (.array, .array): return lhs.rawArray as NSArray == rhs.rawArray as NSArraycase (.dictionary, .dictionary): return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionarycase (.null, .null): return truedefault: return false}}public func >= (lhs: JSON, rhs: JSON) -> Bool {switch (lhs.type, rhs.type) {case (.number, .number): return lhs.rawNumber >= rhs.rawNumbercase (.string, .string): return lhs.rawString >= rhs.rawStringcase (.bool, .bool): return lhs.rawBool == rhs.rawBoolcase (.array, .array): return lhs.rawArray as NSArray == rhs.rawArray as NSArraycase (.dictionary, .dictionary): return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionarycase (.null, .null): return truedefault: return false}}public func > (lhs: JSON, rhs: JSON) -> Bool {switch (lhs.type, rhs.type) {case (.number, .number): return lhs.rawNumber > rhs.rawNumbercase (.string, .string): return lhs.rawString > rhs.rawStringdefault: return false}}public func < (lhs: JSON, rhs: JSON) -> Bool {switch (lhs.type, rhs.type) {case (.number, .number): return lhs.rawNumber < rhs.rawNumbercase (.string, .string): return lhs.rawString < rhs.rawStringdefault: 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)// MARK: - NSNumber: Comparableextension NSNumber {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}}}func == (lhs: NSNumber, rhs: NSNumber) -> Bool {switch (lhs.isBool, rhs.isBool) {case (false, true): return falsecase (true, false): return falsedefault: return lhs.compare(rhs) == .orderedSame}}func != (lhs: NSNumber, rhs: NSNumber) -> Bool {return !(lhs == rhs)}func < (lhs: NSNumber, rhs: NSNumber) -> Bool {switch (lhs.isBool, rhs.isBool) {case (false, true): return falsecase (true, false): return falsedefault: return lhs.compare(rhs) == .orderedAscending}}func > (lhs: NSNumber, rhs: NSNumber) -> Bool {switch (lhs.isBool, rhs.isBool) {case (false, true): return falsecase (true, false): return falsedefault: return lhs.compare(rhs) == ComparisonResult.orderedDescending}}func <= (lhs: NSNumber, rhs: NSNumber) -> Bool {switch (lhs.isBool, rhs.isBool) {case (false, true): return falsecase (true, false): return falsedefault: return lhs.compare(rhs) != .orderedDescending}}func >= (lhs: NSNumber, rhs: NSNumber) -> Bool {switch (lhs.isBool, rhs.isBool) {case (false, true): return falsecase (true, false): return falsedefault: return lhs.compare(rhs) != .orderedAscending}}public enum writingOptionsKeys {case jsonSerializationcase castNilToNSNullcase maxObjextDepthcase encoding}// MARK: - JSON: Codableextension JSON: Codable {private static var codableTypes: [Codable.Type] {return [Bool.self,Int.self,Int8.self,Int16.self,Int32.self,Int64.self,UInt.self,UInt8.self,UInt16.self,UInt32.self,UInt64.self,Double.self,String.self,[JSON].self,[String: JSON].self]}public init(from decoder: Decoder) throws {var object: Any?if let container = try? decoder.singleValueContainer(), !container.decodeNil() {for type in JSON.codableTypes {if object != nil {break}// try to decode valueswitch type {case let boolType as Bool.Type:object = try? container.decode(boolType)case let intType as Int.Type:object = try? container.decode(intType)case let int8Type as Int8.Type:object = try? container.decode(int8Type)case let int32Type as Int32.Type:object = try? container.decode(int32Type)case let int64Type as Int64.Type:object = try? container.decode(int64Type)case let uintType as UInt.Type:object = try? container.decode(uintType)case let uint8Type as UInt8.Type:object = try? container.decode(uint8Type)case let uint16Type as UInt16.Type:object = try? container.decode(uint16Type)case let uint32Type as UInt32.Type:object = try? container.decode(uint32Type)case let uint64Type as UInt64.Type:object = try? container.decode(uint64Type)case let doubleType as Double.Type:object = try? container.decode(doubleType)case let stringType as String.Type:object = try? container.decode(stringType)case let jsonValueArrayType as [JSON].Type:object = try? container.decode(jsonValueArrayType)case let jsonValueDictType as [String: JSON].Type:object = try? container.decode(jsonValueDictType)default:break}}}self.init(object ?? NSNull())}public func encode(to encoder: Encoder) throws {var container = encoder.singleValueContainer()if object is NSNull {try container.encodeNil()return}switch object {case let intValue as Int:try container.encode(intValue)case let int8Value as Int8:try container.encode(int8Value)case let int32Value as Int32:try container.encode(int32Value)case let int64Value as Int64:try container.encode(int64Value)case let uintValue as UInt:try container.encode(uintValue)case let uint8Value as UInt8:try container.encode(uint8Value)case let uint16Value as UInt16:try container.encode(uint16Value)case let uint32Value as UInt32:try container.encode(uint32Value)case let uint64Value as UInt64:try container.encode(uint64Value)case let doubleValue as Double:try container.encode(doubleValue)case let boolValue as Bool:try container.encode(boolValue)case let stringValue as String:try container.encode(stringValue)case is [Any]:let jsonValueArray = array ?? []try container.encode(jsonValueArray)case is [String: Any]:let jsonValueDictValue = dictionary ?? [:]try container.encode(jsonValueDictValue)default:break}}}