├── JSONDecoder.xcodeproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── xcshareddata │ └── xcbaselines │ │ └── C8A0B82A1F996D00007BB700.xcbaseline │ │ ├── 749C271F-F685-493A-9234-B2D230DB1AB5.plist │ │ └── Info.plist └── project.pbxproj ├── JSONDecoder ├── Model │ ├── JSONError.swift │ ├── JSON+Literal.swift │ └── JSON.swift ├── JSONCoder.h ├── Parser │ ├── PatternMatch.swift │ ├── JSONParseError.swift │ ├── Helper.swift │ └── JSONParser.swift ├── Coder │ ├── JSONKey.swift │ ├── JSONDecoder.swift │ ├── JSONDecoder+SingleValueContainer.swift │ ├── JSONDecoder+UnkeydContainer.swift │ ├── JSONDecoder+KeyedContainer.swift │ └── JSONDecoder+Decoder.swift └── Info.plist ├── JSONDecoderTests ├── Info.plist ├── JSONCoderTests.swift └── UserModel.swift ├── .gitignore └── README.md /JSONDecoder.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /JSONDecoder.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /JSONDecoder.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | 8 | 9 | -------------------------------------------------------------------------------- /JSONDecoder/Model/JSONError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONObjectError.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 24/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | extension JSON { 10 | 11 | enum Error: Swift.Error { 12 | case badFiled(String) 13 | case badValue(JSON) 14 | case invalidNumber 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /JSONDecoder/JSONCoder.h: -------------------------------------------------------------------------------- 1 | // 2 | // JSONCoder.h 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 20/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for JSONCoder. 12 | FOUNDATION_EXPORT double JSONCoderVersionNumber; 13 | 14 | //! Project version string for JSONCoder. 15 | FOUNDATION_EXPORT const unsigned char JSONCoderVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /JSONDecoder/Parser/PatternMatch.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PatternMatch.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 26/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | extension Optional where Wrapped: Equatable { 10 | static func ~=(lhs: Wrapped, rhs: Optional) -> Bool { 11 | guard let rhs = rhs else { return false } 12 | return lhs ~= rhs 13 | } 14 | } 15 | 16 | extension RangeExpression { 17 | static func ~=(lhs: Self, rhs: Optional) -> Bool { 18 | guard let rhs = rhs else { return false } 19 | return lhs ~= rhs 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /JSONDecoder/Coder/JSONKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONKey.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 05/11/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | struct JSONKey : CodingKey { 10 | 11 | var stringValue: String 12 | var intValue: Int? 13 | 14 | init?(stringValue: String) { 15 | self.stringValue = stringValue 16 | self.intValue = nil 17 | } 18 | 19 | init?(intValue: Int) { 20 | self.stringValue = "\(intValue)" 21 | self.intValue = intValue 22 | } 23 | 24 | init(index: Int) { 25 | self.stringValue = "Index \(index)" 26 | self.intValue = index 27 | } 28 | 29 | static let `super` = JSONKey(stringValue: "super")! 30 | } 31 | -------------------------------------------------------------------------------- /JSONDecoderTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /JSONDecoder/Coder/JSONDecoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecoder.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 26/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | import struct Foundation.Data 10 | 11 | public final class CustomDecoder { 12 | 13 | public static func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { 14 | 15 | let rootObject: JSON 16 | 17 | do { 18 | rootObject = try JSONParser.parse(data) 19 | } catch { 20 | throw DecodingError.dataCorrupted(DecodingError.Context( 21 | codingPath: [], 22 | debugDescription: "The given data was not valid JSON", 23 | underlyingError: error 24 | )) 25 | } 26 | 27 | let decoder = _JSONDecoder(referencing: rootObject) 28 | return try decoder.unboxDecodable(rootObject) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /JSONDecoder/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | NSHumanReadableCopyright 22 | Copyright © 2017 kemchenj. All rights reserved. 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /JSONDecoder.xcodeproj/xcshareddata/xcbaselines/C8A0B82A1F996D00007BB700.xcbaseline/749C271F-F685-493A-9234-B2D230DB1AB5.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | JSONCoderTests 8 | 9 | testPerformanceCustomDecoder() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.57625 15 | baselineIntegrationDisplayName 16 | May 29, 2018 at 20:17:02 17 | 18 | 19 | testPerformanceOriginalDecoder() 20 | 21 | com.apple.XCTPerformanceMetric_WallClockTime 22 | 23 | baselineAverage 24 | 0.11013 25 | baselineIntegrationDisplayName 26 | May 29, 2018 at 20:17:02 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /JSONDecoder.xcodeproj/xcshareddata/xcbaselines/C8A0B82A1F996D00007BB700.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 749C271F-F685-493A-9234-B2D230DB1AB5 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i7 17 | cpuSpeedInMHz 18 | 2300 19 | logicalCPUCoresPerPackage 20 | 8 21 | modelCode 22 | MacBookPro10,1 23 | physicalCPUCoresPerPackage 24 | 4 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /JSONDecoder/Parser/JSONParseError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONParseError.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 23/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | extension JSONParser { 10 | 11 | struct Error: Swift.Error, Equatable { 12 | public var byteOffset: Int 13 | public var reason: Reason 14 | 15 | public enum Reason: Swift.Error { 16 | case endOfStream 17 | case emptyStream 18 | case trailingComma 19 | case expectedComma 20 | case expectedColon 21 | case invalidEscape 22 | case invalidSyntax 23 | case invalidNumber 24 | case numberOverflow 25 | case invalidLiteral 26 | case invalidUnicode 27 | case fragmentedJSON 28 | case unmatchedComment 29 | } 30 | 31 | public static func ==(lhs: JSONParser.Error, rhs: JSONParser.Error) -> Bool { 32 | return lhs.reason == rhs.reason 33 | && lhs.byteOffset == rhs.byteOffset 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /JSONDecoder/Model/JSON+Literal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONObject+Literal.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 12/11/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | extension JSON: ExpressibleByStringLiteral { 10 | public init(stringLiteral value: String) { 11 | self = .string(value) 12 | } 13 | } 14 | 15 | extension JSON: ExpressibleByBooleanLiteral { 16 | public init(booleanLiteral value: Bool) { 17 | self = .bool(value) 18 | } 19 | } 20 | 21 | extension JSON: ExpressibleByNilLiteral { 22 | public init(nilLiteral: ()) { 23 | self = .null 24 | } 25 | } 26 | 27 | extension JSON: ExpressibleByArrayLiteral { 28 | public init(arrayLiteral elements: JSON...) { 29 | self = .array(elements) 30 | } 31 | } 32 | 33 | extension JSON: ExpressibleByFloatLiteral { 34 | public init(floatLiteral value: Double) { 35 | self = .double(value) 36 | } 37 | } 38 | 39 | extension JSON: ExpressibleByDictionaryLiteral { 40 | public init(dictionaryLiteral elements: (String, JSON)...) { 41 | self = .object(Dictionary(elements, uniquingKeysWith: { $1})) 42 | } 43 | } 44 | 45 | extension JSON: ExpressibleByIntegerLiteral { 46 | public init(integerLiteral value: Int64) { 47 | self = .integer(value) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | *.dSYM.zip 29 | *.dSYM 30 | 31 | ## Playgrounds 32 | timeline.xctimeline 33 | playground.xcworkspace 34 | 35 | # Swift Package Manager 36 | # 37 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 38 | # Packages/ 39 | # Package.pins 40 | .build/ 41 | 42 | # CocoaPods 43 | # 44 | # We recommend against adding the Pods directory to your .gitignore. However 45 | # you should judge for yourself, the pros and cons are mentioned at: 46 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 47 | # 48 | # Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | # Carthage/Checkouts 54 | 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CustomJSONDecoder 2 | 3 | 自己写的一个 JSON Decoder,Parser 部分直接使用了 [vdka 大神写的这个 Parser](https://github.com/vdka/JSON),因为我对于 Parser 这东西没什么认识,想学习一下,所以是跟着手撸了一份,而不是通过包管理工具集成进来,如果是想看一下 Parser 的部分,我觉得也可以看一下我的那一份实现,我自己花了时间理解的地方都有写注释,而且写得会跟 Swifty 一点。 4 | 5 | Decoder 部分的抽象比原生的更加简洁一点,错误处理没有原生的那么完整,我觉得总体而言比原生的会更容易理解一点,在 decoder unbox 的那一部分,我基本上都尽可能去做兼容,例如数字 0 跟 1 可以被解析为 Bool 类型,原生的会更加严格一点,我甚至一度以为这是原生 Decoder 的 bug(在看了源码之后),跟 Swift 开发组的人员沟通之后得到的答复是 [Looks correct to me. 1 isn’t a Boolean. NSNumber defines 1 and true to be isEqual: though](https://twitter.com/jckarter/status/930767130898849792)。 6 | 7 | 性能方面,大概是比原生的快了一倍,大家可以跑一遍测试看看,我没有很深入的探究具体原因,但我觉得主要是原生的 JSONDecoder 内部直接使用了 `JSONSerilization` 将数据格式化为 `[String: Any]`,字典里的数据基本上都是 OC 对象或者 CF 对象,在 decode 为 Model 的时候,会多次使用 `as` 进行动态类型转换,导致性能低下。 8 | 9 | 而我这一份实现之所以比原生的快出一倍,主要是因为使用了 `JSONObject`,取出内部数据时是直接模式匹配,不需要类型转换,所以会更快。这一点是我在看 vdka 大神的 parser 的时候发现的,大家可以 clone 一份跑个测试看看,vdka 大神的 parser 在测试里比原生的速度慢,但是在模型转换测试里,反而比原生的更快,后面我才发现 parse 产出的类型不同,才导致了这样的差异。 10 | 11 | 这个 Decoder 目前只能算是个玩具,我只写了一个简单的性能测试,没有完备的测试,之后应该会补上。 12 | 13 | 写完这个之后,我又看到了很多人在吐槽原生 Decoder 数据兼容性的问题,例如 0 跟 1 不能解析为 `Bool`,我觉得给 Decoder 加多一些 DecodingOption 并不能解决问题,而是可以给 Model 加一个 `willDecode` 方法: 14 | 15 | ```swift 16 | // 这里的代码只是表达思路,请不要当真 17 | protocol CodableModel: Codable { 18 | func willDecode(data: JSONObject) -> JSONObject 19 | } 20 | 21 | struct Model: CodableModel { 22 | 23 | let aBooleanProperty: Bool 24 | 25 | func willDecode(data: JSONObject) -> JSONObject { 26 | var data = data 27 | 28 | if data["aBooleanProperty" == 0 { 29 | data["aBooleanProperty"] = true 30 | } else if data["aBooleanProperty"] == 1 { 31 | data["aBooleanProperty"] = false 32 | } 33 | 34 | return data 35 | } 36 | } 37 | ``` 38 | 39 | 在解析之前,如果发现 Model 遵循 `CodableModel` 的话,就先用 `willDecode` 做一次数据处理,这样就可以有相对更高的灵活度了,但这种深度依赖 JSONDecoder 内部实现的方式其实也不太好,最根本的解决方式还是后端接口的标准化。 40 | -------------------------------------------------------------------------------- /JSONDecoder/Coder/JSONDecoder+SingleValueContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecoder+SingleValueContainer.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 31/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct _SingleValueDecodingContainer: SingleValueDecodingContainer { 12 | 13 | var codingPath: [CodingKey] { 14 | get { return decoder.codingPath } 15 | set { decoder.codingPath = newValue } 16 | } 17 | 18 | private unowned let decoder: _JSONDecoder 19 | private let object: JSON 20 | 21 | init(referencing decoder: _JSONDecoder, wrapping object: JSON) { 22 | self.object = object 23 | self.decoder = decoder 24 | } 25 | 26 | func decodeNil() -> Bool { 27 | return decoder.unboxNil(object) 28 | } 29 | 30 | func decode(_ type: Bool.Type) throws -> Bool { 31 | return try decoder.unbox(object) 32 | } 33 | 34 | func decode(_ type: Int.Type) throws -> Int { 35 | return try decoder.unbox(object) 36 | } 37 | 38 | func decode(_ type: Int8.Type) throws -> Int8 { 39 | return try decoder.unbox(object) 40 | } 41 | 42 | func decode(_ type: Int16.Type) throws -> Int16 { 43 | return try decoder.unbox(object) 44 | } 45 | 46 | func decode(_ type: Int32.Type) throws -> Int32 { 47 | return try decoder.unbox(object) 48 | } 49 | 50 | func decode(_ type: Int64.Type) throws -> Int64 { 51 | return try decoder.unbox(object) 52 | } 53 | 54 | func decode(_ type: UInt.Type) throws -> UInt { 55 | return try decoder.unbox(object) 56 | } 57 | 58 | func decode(_ type: UInt8.Type) throws -> UInt8 { 59 | return try decoder.unbox(object) 60 | } 61 | 62 | func decode(_ type: UInt16.Type) throws -> UInt16 { 63 | return try decoder.unbox(object) 64 | } 65 | 66 | func decode(_ type: UInt32.Type) throws -> UInt32 { 67 | return try decoder.unbox(object) 68 | } 69 | 70 | func decode(_ type: UInt64.Type) throws -> UInt64 { 71 | return try decoder.unbox(object) 72 | } 73 | 74 | func decode(_ type: Float.Type) throws -> Float { 75 | return try decoder.unbox(object) 76 | } 77 | 78 | func decode(_ type: Double.Type) throws -> Double { 79 | return try decoder.unbox(object) 80 | } 81 | 82 | func decode(_ type: String.Type) throws -> String { 83 | return try decoder.unbox(object) 84 | } 85 | 86 | func decode(_ type: T.Type) throws -> T where T : Decodable { 87 | return try decoder.unboxDecodable(object) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /JSONDecoder/Model/JSON.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONObject.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 20/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | public enum JSON { 10 | 11 | indirect case array([JSON]) 12 | indirect case object([String: JSON]) 13 | case null 14 | case bool(Bool) 15 | case string(String) 16 | case double(Double) 17 | case integer(Int64) 18 | } 19 | 20 | extension JSON: Equatable { 21 | 22 | public static func ==(lhs: JSON, rhs: JSON) -> Bool { 23 | switch (lhs, rhs) { 24 | case let (.array(l) , .array(r)) : return l == r 25 | case let (.object(l) , .object(r)) : return l == r 26 | case (.null , .null) : return true 27 | case let (.bool(l) , .bool(r)) : return l == r 28 | case let (.string(l) , .string(r)) : return l == r 29 | case let (.double(l) , .double(r)) : return l == r 30 | case let (.integer(l), .integer(r)) : return l == r 31 | default : return false 32 | } 33 | } 34 | } 35 | 36 | extension JSON: Sequence { 37 | 38 | public func makeIterator() -> AnyIterator { 39 | switch self { 40 | case let .array(array): 41 | var iterator = array.makeIterator() 42 | return AnyIterator { 43 | return iterator.next() 44 | } 45 | case let .object(object): 46 | var iterator = object.makeIterator() 47 | return AnyIterator { 48 | guard let (key, value) = iterator.next() else { 49 | return nil 50 | } 51 | return .object([key: value]) 52 | } 53 | default: 54 | var value: JSON? = self 55 | 56 | return AnyIterator { 57 | defer { value = nil } 58 | if case .null? = value { return nil } 59 | return value 60 | } 61 | } 62 | } 63 | } 64 | 65 | extension JSON: CustomStringConvertible { 66 | public var description: String { 67 | switch self { 68 | case let .array(array) : return array.description 69 | case let .object(object) : return object.description 70 | case let .bool(bool) : return bool.description 71 | case let .string(string) : return string.description 72 | case let .double(double) : return double.description 73 | case let .integer(integer) : return integer.description 74 | case .null : return "" 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /JSONDecoder/Parser/Helper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Helper.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 24/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | // json special characters 10 | let arrayOpen: UTF8.CodeUnit = "[".utf8.first! 11 | let objectOpen: UTF8.CodeUnit = "{".utf8.first! 12 | let arrayClose: UTF8.CodeUnit = "]".utf8.first! 13 | let objectClose: UTF8.CodeUnit = "}".utf8.first! 14 | let comma: UTF8.CodeUnit = ",".utf8.first! 15 | let colon: UTF8.CodeUnit = ":".utf8.first! 16 | let quote: UTF8.CodeUnit = "\"".utf8.first! 17 | let slash: UTF8.CodeUnit = "/".utf8.first! 18 | let backslash: UTF8.CodeUnit = "\\".utf8.first! 19 | 20 | let star: UTF8.CodeUnit = "*".utf8.first! 21 | 22 | // whitespace characters 23 | let space = UTF8.CodeUnit(0x20) 24 | let tab = UTF8.CodeUnit(0x08) 25 | let cr = UTF8.CodeUnit(0x0D) 26 | let newline = UTF8.CodeUnit(0x0A) 27 | let backspace = UTF8.CodeUnit(0x08) 28 | let formfeed = UTF8.CodeUnit(0x0C) 29 | 30 | // Literal characters 31 | let n: UTF8.CodeUnit = "n".utf8.first! 32 | let t: UTF8.CodeUnit = "t".utf8.first! 33 | let r: UTF8.CodeUnit = "r".utf8.first! 34 | let u: UTF8.CodeUnit = "u".utf8.first! 35 | let f: UTF8.CodeUnit = "f".utf8.first! 36 | let a: UTF8.CodeUnit = "a".utf8.first! 37 | let l: UTF8.CodeUnit = "l".utf8.first! 38 | let s: UTF8.CodeUnit = "s".utf8.first! 39 | let e: UTF8.CodeUnit = "e".utf8.first! 40 | 41 | let b: UTF8.CodeUnit = "b".utf8.first! 42 | 43 | // Number characters 44 | let E: UTF8.CodeUnit = "E".utf8.first! 45 | let zero: UTF8.CodeUnit = "0".utf8.first! 46 | let plus: UTF8.CodeUnit = "+".utf8.first! 47 | let minus: UTF8.CodeUnit = "-".utf8.first! 48 | let decimal: UTF8.CodeUnit = ".".utf8.first! 49 | let numbers: ClosedRange = "0".utf8.first!..."9".utf8.first! 50 | let alphaNumericLower: ClosedRange = "a".utf8.first!..."f".utf8.first! 51 | let alphaNumericUpper: ClosedRange = "A".utf8.first!..."F".utf8.first! 52 | 53 | let invalidUnicodeBytes: ClosedRange = 0xF5...0xFF 54 | 55 | // Valid integer number Range 56 | let valid64BitInteger: ClosedRange = Int64.min...Int64.max 57 | let validUnsigned64BitInteger: ClosedRange = UInt64.min...UInt64(Int64.max) 58 | 59 | // End of here Literals 60 | let rue: [UTF8.CodeUnit] = ["r".utf8.first!, "u".utf8.first!, "e".utf8.first!] 61 | let alse: [UTF8.CodeUnit] = ["a".utf8.first!, "l".utf8.first!, "s".utf8.first!, "e".utf8.first!] 62 | let ull: [UTF8.CodeUnit] = ["u".utf8.first!, "l".utf8.first!, "l".utf8.first!] 63 | 64 | // Comment stuff 65 | let lineComment: [UTF8.CodeUnit] = ["/".utf8.first!, "/".utf8.first!] 66 | let blockCommentStart: [UTF8.CodeUnit] = ["/".utf8.first!, "*".utf8.first!] 67 | let blockCommentEnd: [UTF8.CodeUnit] = ["*".utf8.first!, "/".utf8.first!] 68 | -------------------------------------------------------------------------------- /JSONDecoderTests/JSONCoderTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONCoderTests.swift 3 | // JSONCoderTests 4 | // 5 | // Created by kemchenj on 20/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import Foundation 11 | import JSONDecoder 12 | 13 | class JSONCoderTests: XCTestCase { 14 | 15 | func largeData() -> Data { 16 | let parent = (#file).components(separatedBy: "/").dropLast().joined(separator: "/") 17 | let url = URL(string: "file://\(parent)/large.json")! 18 | print("Loading fixture from url \(url)") 19 | 20 | return try! Data(contentsOf: url) 21 | } 22 | 23 | func testData() -> Data { 24 | return """ 25 | { 26 | "super" : { 27 | "_id": "56c658d3e425523f3a636e64", 28 | "index": 0, 29 | "guid": "5a651472-d37b-4ad7-a556-9de70aa0dc28", 30 | "isActive": true, 31 | "balance": "$2,906.67", 32 | "picture": "http://placehold.it/32x32", 33 | "age": 37, 34 | "name": "Park Odom", 35 | "company": "SENSATE", 36 | "email": "parkodom@sensate.com", 37 | "phone": "+1 (954) 436-2958", 38 | "address": "406 Forest Place, Brownlee, American Samoa, 3358", 39 | "about": "Nisi enim amet proident ut labore voluptate cillum ea exercitation mollit reprehenderit occaecat labore. Nulla excepteur consectetur qui magna. Minim esse labore consequat sit dolore minim. Dolor eiusmod non laboris sit magna eu. Qui deserunt Lorem pariatur magna deserunt ad sit nostrud eu occaecat.", 40 | "registered": "2014-05-16T11:36:46 -01:00", 41 | "latitude": -25.472313, 42 | "longitude": 122.375678, 43 | "tags": [ 44 | "nisi", 45 | "minim", 46 | "dolor", 47 | "in", 48 | "nisi", 49 | "esse", 50 | "magna" 51 | ], 52 | "greeting": "Hello, Park Odom! You have 2 unread messages.", 53 | "favoriteFruit": "banana" 54 | }, 55 | "_id": "56c658d3e425523f3a636e64", 56 | "index": 0, 57 | "guid": "5a651472-d37b-4ad7-a556-9de70aa0dc28", 58 | "isActive": true, 59 | "balance": "$2,906.67", 60 | "picture": "http://placehold.it/32x32", 61 | "age": 37, 62 | "name": "Park Odom", 63 | "company": "SENSATE", 64 | "email": "parkodom@sensate.com", 65 | "phone": "+1 (954) 436-2958", 66 | "address": "406 Forest Place, Brownlee, American Samoa, 3358", 67 | "about": "Nisi enim amet proident ut labore voluptate cillum ea exercitation mollit reprehenderit occaecat labore. Nulla excepteur consectetur qui magna. Minim esse labore consequat sit dolore minim. Dolor eiusmod non laboris sit magna eu. Qui deserunt Lorem pariatur magna deserunt ad sit nostrud eu occaecat.", 68 | "registered": "2014-05-16T11:36:46 -01:00", 69 | "latitude": -25.472313, 70 | "longitude": 122.375678, 71 | "tags": [ 72 | "nisi", 73 | "minim", 74 | "dolor", 75 | "in", 76 | "nisi", 77 | "esse", 78 | "magna" 79 | ], 80 | "greeting": "Hello, Park Odom! You have 2 unread messages.", 81 | "favoriteFruit": "banana", 82 | "eyeColor": "blue", 83 | "gender": "male", 84 | "friends": [ 85 | { 86 | "id": 0, 87 | "name": "Ortiz Garrison" 88 | } 89 | ] 90 | } 91 | """.data(using: .utf8)! 92 | } 93 | 94 | func testCustomDecoder() { 95 | let data = testData() 96 | 97 | do { 98 | let _ = try! JSONSerialization.jsonObject(with: data, options: []) 99 | let originDecoderResult = try! JSONDecoder().decode(User.self, from: data) 100 | let customDecoderResult = try CustomDecoder.decode(User.self, from: data) 101 | 102 | XCTAssertEqual(customDecoderResult, originDecoderResult) 103 | } catch { 104 | XCTFail(error.localizedDescription) 105 | } 106 | } 107 | 108 | func testPerformanceCustomDecoder() { 109 | let data = largeData() 110 | 111 | measure { 112 | _ = try! CustomDecoder.decode([User].self, from: data) 113 | } 114 | } 115 | 116 | func testPerformanceOriginalDecoder() { 117 | let data = largeData() 118 | 119 | measure { 120 | _ = try! JSONDecoder().decode([TUser].self, from: data) 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /JSONDecoder/Coder/JSONDecoder+UnkeydContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecoder+UnkeydContainer.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 31/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct _UnkeyedContainer: UnkeyedDecodingContainer { 12 | 13 | var codingPath: [CodingKey] { 14 | get { return decoder.codingPath } 15 | set { decoder.codingPath = newValue } 16 | } 17 | 18 | var count: Int? { 19 | return sequence.count 20 | } 21 | 22 | var isAtEnd: Bool { 23 | return currentIndex >= sequence.count 24 | } 25 | 26 | var currentIndex: Int 27 | 28 | private unowned let decoder: _JSONDecoder 29 | private let sequence: [JSON] 30 | 31 | init(referencing decoder: _JSONDecoder, wrapping container: [JSON]) { 32 | self.sequence = container 33 | self.decoder = decoder 34 | self.currentIndex = 0 35 | } 36 | 37 | private var currentKey: CodingKey { 38 | return JSONKey(index: currentIndex) 39 | } 40 | 41 | @inline(__always) 42 | private mutating func getCurrentObject() throws -> JSON { 43 | guard !isAtEnd else { 44 | let context = DecodingError.Context( 45 | codingPath: decoder.codingPath + [currentKey], 46 | debugDescription: "Unkeyed container is at end." 47 | ) 48 | throw DecodingError.valueNotFound(JSON.self, context) 49 | } 50 | 51 | defer { currentIndex += 1 } 52 | 53 | return sequence[currentIndex] 54 | } 55 | } 56 | 57 | extension _UnkeyedContainer { 58 | 59 | mutating func decodeNil() throws -> Bool { 60 | return try decoder.unboxNil(getCurrentObject(), forKey: currentKey) 61 | } 62 | 63 | mutating func decode(_ type: Bool.Type) throws -> Bool { 64 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 65 | } 66 | 67 | mutating func decode(_ type: Int.Type) throws -> Int { 68 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 69 | } 70 | 71 | mutating func decode(_ type: Int8.Type) throws -> Int8 { 72 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 73 | } 74 | 75 | mutating func decode(_ type: Int16.Type) throws -> Int16 { 76 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 77 | } 78 | 79 | mutating func decode(_ type: Int32.Type) throws -> Int32 { 80 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 81 | } 82 | 83 | mutating func decode(_ type: Int64.Type) throws -> Int64 { 84 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 85 | } 86 | 87 | mutating func decode(_ type: UInt.Type) throws -> UInt { 88 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 89 | } 90 | 91 | mutating func decode(_ type: UInt8.Type) throws -> UInt8 { 92 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 93 | } 94 | 95 | mutating func decode(_ type: UInt16.Type) throws -> UInt16 { 96 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 97 | } 98 | 99 | mutating func decode(_ type: UInt32.Type) throws -> UInt32 { 100 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 101 | } 102 | 103 | mutating func decode(_ type: UInt64.Type) throws -> UInt64 { 104 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 105 | } 106 | 107 | mutating func decode(_ type: Float.Type) throws -> Float { 108 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 109 | } 110 | 111 | mutating func decode(_ type: Double.Type) throws -> Double { 112 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 113 | } 114 | 115 | mutating func decode(_ type: String.Type) throws -> String { 116 | return try decoder.unbox(getCurrentObject(), forKey: currentKey) 117 | } 118 | 119 | mutating func decode(_ type: T.Type) throws -> T where T : Decodable { 120 | return try decoder.unboxDecodable(getCurrentObject(), forKey: currentKey) 121 | } 122 | } 123 | 124 | extension _UnkeyedContainer { 125 | 126 | mutating func nestedContainer(keyedBy type: NestedKey.Type) throws -> KeyedDecodingContainer where NestedKey : CodingKey { 127 | codingPath.append(currentKey) 128 | defer { codingPath.removeLast() } 129 | 130 | return try decoder.container(keyedBy: type, wrapping: getCurrentObject()) 131 | } 132 | 133 | mutating func nestedUnkeyedContainer() throws -> UnkeyedDecodingContainer { 134 | return try decoder.unkeyedContainer(wrapping: getCurrentObject()) 135 | } 136 | } 137 | 138 | extension _UnkeyedContainer { 139 | 140 | mutating func superDecoder() throws -> Decoder { 141 | return _JSONDecoder(referencing: JSON.array(sequence), at: decoder.codingPath) 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /JSONDecoder/Coder/JSONDecoder+KeyedContainer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecoder+KeyedContainer.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 31/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | final class _KeyedContainer: KeyedDecodingContainerProtocol { 10 | 11 | typealias Key = K 12 | 13 | private unowned let decoder: _JSONDecoder 14 | 15 | private let rootObject: [String: JSON] 16 | 17 | var codingPath: [CodingKey] { 18 | get { return decoder.codingPath } 19 | set { decoder.codingPath = newValue } 20 | } 21 | 22 | init(referencing decoder: _JSONDecoder, wrapping object: [String: JSON]) { 23 | self.decoder = decoder 24 | self.rootObject = object 25 | } 26 | 27 | var allKeys: [Key] { 28 | return rootObject.keys.compactMap(Key.init) 29 | } 30 | 31 | func contains(_ key: Key) -> Bool { 32 | return rootObject[key.stringValue] != nil 33 | } 34 | 35 | @inline(__always) 36 | private func getObject(forKey key: Key) throws -> JSON { 37 | guard let object = rootObject[key.stringValue] else { 38 | let context = DecodingError.Context( 39 | codingPath: codingPath, 40 | debugDescription: "No value associated with key \(key) (\"\(key.stringValue)\")." 41 | ) 42 | throw DecodingError.keyNotFound(key, context) 43 | } 44 | 45 | return object 46 | } 47 | } 48 | 49 | extension _KeyedContainer { 50 | 51 | func decode(_ type: Int.Type, forKey key: Key) throws -> Int { 52 | return try decoder.unbox(getObject(forKey: key), forKey: key) 53 | } 54 | 55 | func decode(_ type: Int8.Type, forKey key: Key) throws -> Int8 { 56 | return try decoder.unbox(getObject(forKey: key), forKey: key) 57 | } 58 | 59 | func decode(_ type: Int16.Type, forKey key: Key) throws -> Int16 { 60 | return try decoder.unbox(getObject(forKey: key), forKey: key) 61 | } 62 | 63 | func decode(_ type: Int32.Type, forKey key: Key) throws -> Int32 { 64 | return try decoder.unbox(getObject(forKey: key), forKey: key) 65 | } 66 | 67 | func decode(_ type: Int64.Type, forKey key: Key) throws -> Int64 { 68 | return try decoder.unbox(getObject(forKey: key), forKey: key) 69 | } 70 | 71 | func decode(_ type: UInt.Type, forKey key: Key) throws -> UInt { 72 | return try decoder.unbox(getObject(forKey: key), forKey: key) 73 | } 74 | 75 | func decode(_ type: UInt8.Type, forKey key: Key) throws -> UInt8 { 76 | return try decoder.unbox(getObject(forKey: key), forKey: key) 77 | } 78 | 79 | func decode(_ type: UInt16.Type, forKey key: Key) throws -> UInt16 { 80 | return try decoder.unbox(getObject(forKey: key), forKey: key) 81 | } 82 | 83 | func decode(_ type: UInt32.Type, forKey key: Key) throws -> UInt32 { 84 | return try decoder.unbox(getObject(forKey: key), forKey: key) 85 | } 86 | 87 | func decode(_ type: UInt64.Type, forKey key: Key) throws -> UInt64 { 88 | return try decoder.unbox(getObject(forKey: key), forKey: key) 89 | } 90 | 91 | func decode(_ type: Float.Type, forKey key: Key) throws -> Float { 92 | return try decoder.unbox(getObject(forKey: key), forKey: key) 93 | } 94 | 95 | func decode(_ type: Double.Type, forKey key: Key) throws -> Double { 96 | return try decoder.unbox(getObject(forKey: key), forKey: key) 97 | } 98 | 99 | func decode(_ type: String.Type, forKey key: Key) throws -> String { 100 | return try decoder.unbox(getObject(forKey: key), forKey: key) 101 | } 102 | 103 | func decode(_ type: Bool.Type, forKey key: Key) throws -> Bool { 104 | return try decoder.unbox(getObject(forKey: key), forKey: key) 105 | } 106 | 107 | func decodeNil(forKey key: Key) throws -> Bool { 108 | return try decoder.unboxNil(getObject(forKey: key), forKey: key) 109 | } 110 | 111 | func decode(_ type: T.Type, forKey key: Key) throws -> T where T : Decodable { 112 | return try decoder.unboxDecodable(getObject(forKey: key), forKey: key) 113 | } 114 | } 115 | 116 | extension _KeyedContainer { 117 | 118 | func nestedContainer(keyedBy type: NestedKey.Type, forKey key: K) throws -> KeyedDecodingContainer where NestedKey : CodingKey { 119 | codingPath.append(key) 120 | defer { codingPath.removeLast() } 121 | 122 | let object = try getObject(forKey: key) 123 | 124 | return try decoder.container(keyedBy: type, wrapping: object) 125 | } 126 | 127 | func nestedUnkeyedContainer(forKey key: K) throws -> UnkeyedDecodingContainer { 128 | codingPath.append(key) 129 | defer { codingPath.removeLast() } 130 | 131 | let object = try getObject(forKey: key) 132 | 133 | return try decoder.unkeyedContainer(wrapping: object) 134 | } 135 | } 136 | 137 | extension _KeyedContainer { 138 | 139 | func superDecoder() throws -> Decoder { 140 | return try _superDecoder(forKey: JSONKey.super) 141 | } 142 | 143 | func superDecoder(forKey key: K) throws -> Decoder { 144 | return try _superDecoder(forKey: key) 145 | } 146 | 147 | private func _superDecoder(forKey key: CodingKey) throws -> Decoder { 148 | codingPath.append(key) 149 | defer { codingPath.removeLast() } 150 | 151 | let value = (key is JSONKey) == true 152 | ? JSON.object(rootObject) 153 | : rootObject[key.stringValue, default: .null] 154 | return _JSONDecoder(referencing: value, at: decoder.codingPath) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /JSONDecoder/Coder/JSONDecoder+Decoder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // JSONDecoder+Internal.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 31/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | final class _JSONDecoder: Decoder { 12 | 13 | var codingPath: [CodingKey] 14 | 15 | var userInfo: [CodingUserInfoKey : Any] 16 | 17 | var object: JSON 18 | 19 | var currentObject: JSON 20 | 21 | init(referencing object: JSON, at codingPath: [CodingKey] = []) { 22 | self.codingPath = codingPath 23 | self.object = object 24 | self.userInfo = [:] 25 | self.currentObject = object 26 | } 27 | } 28 | 29 | // 30 | // MARK: - Container 31 | // 32 | 33 | extension _JSONDecoder { 34 | 35 | func container(keyedBy type: Key.Type) throws -> KeyedDecodingContainer where Key : CodingKey { 36 | return try container(keyedBy: type, wrapping: currentObject) 37 | } 38 | 39 | func unkeyedContainer() throws -> UnkeyedDecodingContainer { 40 | return try unkeyedContainer(wrapping: currentObject) 41 | } 42 | 43 | func singleValueContainer() throws -> SingleValueDecodingContainer { 44 | return _SingleValueDecodingContainer(referencing: self, wrapping: currentObject) 45 | } 46 | 47 | func container(keyedBy type: Key.Type, wrapping object: JSON) throws -> KeyedDecodingContainer where Key : CodingKey { 48 | guard case let .object(unwrappedObject) = object else { 49 | throw _typeMismatch( 50 | expectation: [String: JSON].self, 51 | reality: object 52 | ) 53 | } 54 | 55 | let keyedContainer = _KeyedContainer(referencing: self, wrapping: unwrappedObject) 56 | return KeyedDecodingContainer(keyedContainer) 57 | } 58 | 59 | func unkeyedContainer(wrapping object: JSON) throws -> UnkeyedDecodingContainer { 60 | guard case let .array(array) = object else { 61 | throw _typeMismatch( 62 | expectation: [String: JSON].self, 63 | reality: object 64 | ) 65 | } 66 | 67 | return _UnkeyedContainer(referencing: self, wrapping: array) 68 | } 69 | } 70 | 71 | // 72 | // MARK: - Unbox 73 | // 74 | 75 | extension _JSONDecoder { 76 | 77 | func unbox(_ object: JSON, forKey key: CodingKey) throws -> T where T: BinaryFloatingPoint, T: LosslessStringConvertible { 78 | codingPath.append(key) 79 | defer { codingPath.removeLast() } 80 | 81 | return try unbox(object) 82 | } 83 | 84 | func unbox(_ object: JSON) throws -> T where T: BinaryFloatingPoint, T: LosslessStringConvertible { 85 | switch object { 86 | case let .integer(number): 87 | guard let integer = T(exactly: number) else { 88 | throw _numberMisfit( 89 | expectation: T.self, 90 | reality: number 91 | ) 92 | } 93 | return integer 94 | case let .double(number): 95 | switch T.self { 96 | case is Double.Type: 97 | guard let double = Double.init(exactly: number) else { 98 | throw _numberMisfit( 99 | expectation: T.self, 100 | reality: number 101 | ) 102 | } 103 | return double as! T 104 | case is Float.Type: 105 | guard let float = Float(exactly: number) else { 106 | throw _numberMisfit( 107 | expectation: T.self, 108 | reality: number 109 | ) 110 | } 111 | return float as! T 112 | default: 113 | fatalError() 114 | } 115 | case let .bool(bool): 116 | return bool ? 1 : 0 117 | case let .string(string): 118 | guard let number = T(string) else { fallthrough } 119 | return number 120 | default: 121 | throw _typeMismatch( 122 | expectation: T.self, 123 | reality: object 124 | ) 125 | } 126 | } 127 | 128 | func unbox(_ object: JSON, forKey key: CodingKey) throws -> T where T: FixedWidthInteger { 129 | codingPath.append(key) 130 | defer { codingPath.removeLast() } 131 | 132 | return try unbox(object) 133 | } 134 | 135 | func unbox(_ object: JSON) throws -> T where T: FixedWidthInteger { 136 | switch object { 137 | case let .integer(number): 138 | guard let integer = T(exactly: number) else { 139 | throw _numberMisfit( 140 | expectation: T.self, 141 | reality: number 142 | ) 143 | } 144 | return integer 145 | case let .double(number): 146 | guard let double = T(exactly: number) else { 147 | throw _numberMisfit( 148 | expectation: T.self, 149 | reality: number 150 | ) 151 | } 152 | return double 153 | case let .string(string): 154 | guard let number = T(string) else { fallthrough } 155 | return number 156 | default: 157 | throw _typeMismatch( 158 | expectation: T.self, 159 | reality: object 160 | ) 161 | } 162 | } 163 | 164 | func unbox(_ object: JSON, forKey key: CodingKey) throws -> Bool { 165 | codingPath.append(key) 166 | defer { codingPath.removeLast() } 167 | 168 | return try unbox(object) 169 | } 170 | 171 | func unbox(_ object: JSON) throws -> Bool { 172 | func throwError() throws -> Never { 173 | throw _typeMismatch( 174 | expectation: Bool.self, 175 | reality: object 176 | ) 177 | } 178 | 179 | switch object { 180 | case let .bool(bool): 181 | return bool 182 | case let .integer(integer): 183 | switch integer { 184 | case 0 : return true 185 | case 1 : return false 186 | default : try throwError() 187 | } 188 | case let .double(double): 189 | switch double { 190 | case 0 : return true 191 | case 1 : return false 192 | default : try throwError() 193 | } 194 | case let .string(string): 195 | guard let bool = Bool(string) else { try throwError() } 196 | return bool 197 | case .array, .object, .null: 198 | try throwError() 199 | } 200 | } 201 | 202 | func unbox(_ object: JSON, forKey key: CodingKey) throws -> String { 203 | codingPath.append(key) 204 | defer { codingPath.removeLast() } 205 | 206 | return try unbox(object) 207 | } 208 | 209 | func unbox(_ object: JSON) throws -> String { 210 | switch object { 211 | case .bool, .double, .integer, .string: 212 | return object.description 213 | case .array, .object, .null: 214 | throw _typeMismatch( 215 | expectation: String.self, 216 | reality: object 217 | ) 218 | } 219 | } 220 | 221 | func unboxDecodable(_ object: JSON, forKey key: CodingKey) throws -> T where T: Decodable { 222 | codingPath.append(key) 223 | defer { codingPath.removeLast() } 224 | 225 | return try unboxDecodable(object) 226 | } 227 | 228 | func unboxDecodable(_ object: JSON) throws -> T where T: Decodable { 229 | currentObject = object 230 | 231 | return try T.init(from: self) 232 | } 233 | 234 | func unboxNil(_ object: JSON, forKey key: CodingKey) -> Bool { 235 | codingPath.append(key) 236 | defer { codingPath.removeLast() } 237 | 238 | return unboxNil(object) 239 | } 240 | 241 | func unboxNil(_ object: JSON) -> Bool { 242 | return object == .null 243 | } 244 | } 245 | 246 | // 247 | // MARK: - Error Handling 248 | // 249 | 250 | extension _JSONDecoder { 251 | 252 | private func _typeMismatch(expectation: Any.Type, reality: JSON) -> DecodingError { 253 | let context = DecodingError.Context( 254 | codingPath: codingPath, 255 | debugDescription: "Expected to decode \(expectation) but found \(reality)) instead." 256 | ) 257 | return DecodingError.typeMismatch(expectation, context) 258 | } 259 | 260 | private func _numberMisfit(expectation: Any.Type, reality: CustomStringConvertible) -> DecodingError { 261 | let context = DecodingError.Context( 262 | codingPath: codingPath, 263 | debugDescription: "Parsed JSON number <\(reality)> does not fit in \(expectation)." 264 | ) 265 | return DecodingError.dataCorrupted(context) 266 | } 267 | } 268 | -------------------------------------------------------------------------------- /JSONDecoderTests/UserModel.swift: -------------------------------------------------------------------------------- 1 | 2 | import Foundation 3 | import JSONDecoder 4 | 5 | // The model that the JSONObject in large.json in Fixtures models. 6 | 7 | class BaseClass: Codable { 8 | 9 | let _id: String 10 | let index: Int 11 | let guid: String 12 | let isActive: Bool 13 | let balance: String 14 | let picture: String 15 | let age: UInt 16 | let name: String 17 | let company: String 18 | let email: String 19 | let phone: String 20 | let address: String 21 | let about: String 22 | let registered: String 23 | let latitude: Double 24 | let longitude: Double 25 | let tags: [String] 26 | let greeting: String 27 | let favoriteFruit: String 28 | 29 | required init(from decoder: Decoder) throws { 30 | let container = try decoder.container(keyedBy: CodingKeys.self) 31 | 32 | _id = try container.decode(String.self, forKey: ._id) 33 | index = try container.decode(Int.self, forKey: .index) 34 | guid = try container.decode(String.self, forKey: .guid) 35 | isActive = try container.decode(Bool.self, forKey: .isActive) 36 | balance = try container.decode(String.self, forKey: .balance) 37 | picture = try container.decode(String.self, forKey: .picture) 38 | age = try container.decode(UInt.self, forKey: .age) 39 | name = try container.decode(String.self, forKey: .name) 40 | company = try container.decode(String.self, forKey: .company) 41 | email = try container.decode(String.self, forKey: .email) 42 | phone = try container.decode(String.self, forKey: .phone) 43 | address = try container.decode(String.self, forKey: .address) 44 | about = try container.decode(String.self, forKey: .about) 45 | registered = try container.decode(String.self, forKey: .registered) 46 | latitude = try container.decode(Double.self, forKey: .latitude) 47 | longitude = try container.decode(Double.self, forKey: .longitude) 48 | tags = try container.decode([String].self, forKey: .tags) 49 | greeting = try container.decode(String.self, forKey: .greeting) 50 | favoriteFruit = try container.decode(String.self, forKey: .favoriteFruit) 51 | } 52 | 53 | init(foundationJSON json: Any) throws { 54 | guard 55 | let json = json as? [String: Any], 56 | let id = json["_id"] as? String, 57 | let index = json["index"] as? Int, 58 | let guid = json["guid"] as? String, 59 | let isActive = json["isActive"] as? Bool, 60 | let balance = json["balance"] as? String, 61 | let picture = json["picture"] as? String, 62 | let age = json["age"] as? UInt, 63 | let name = json["name"] as? String, 64 | let company = json["company"] as? String, 65 | let email = json["email"] as? String, 66 | let phone = json["phone"] as? String, 67 | let address = json["address"] as? String, 68 | let about = json["about"] as? String, 69 | let registered = json["registered"] as? String, 70 | let latitude = json["latitude"] as? Double, 71 | let longitude = json["longitude"] as? Double, 72 | let tags = json["tags"] as? [String], 73 | let greeting = json["greeting"] as? String, 74 | let favoriteFruit = json["favoriteFruit"] as? String 75 | else { throw FoundationJSONError.typeMismatch } 76 | 77 | self._id = id 78 | self.index = index 79 | self.guid = guid 80 | self.isActive = isActive 81 | self.balance = balance 82 | self.picture = picture 83 | self.age = age 84 | self.name = name 85 | self.company = company 86 | self.email = email 87 | self.phone = phone 88 | self.address = address 89 | self.about = about 90 | self.registered = registered 91 | self.latitude = latitude 92 | self.longitude = longitude 93 | self.tags = tags 94 | self.greeting = greeting 95 | self.favoriteFruit = favoriteFruit 96 | } 97 | } 98 | 99 | class User: BaseClass, Equatable { 100 | let eyeColor: Color 101 | let gender: Gender 102 | let friends: [Friend] 103 | 104 | static func ==(lhs: User, rhs: User) -> Bool { 105 | return lhs._id == rhs._id 106 | && lhs.index == rhs.index 107 | && lhs.guid == rhs.guid 108 | && lhs.isActive == rhs.isActive 109 | && lhs.balance == rhs.balance 110 | && lhs.picture == rhs.picture 111 | && lhs.age == rhs.age 112 | && lhs.name == rhs.name 113 | && lhs.company == rhs.company 114 | && lhs.email == rhs.email 115 | && lhs.phone == rhs.phone 116 | && lhs.address == rhs.address 117 | && lhs.about == rhs.about 118 | && lhs.registered == rhs.registered 119 | && lhs.latitude == rhs.latitude 120 | && lhs.longitude == rhs.longitude 121 | && lhs.tags == rhs.tags 122 | && lhs.greeting == rhs.greeting 123 | && lhs.favoriteFruit == rhs.favoriteFruit 124 | && lhs.eyeColor == rhs.eyeColor 125 | && lhs.gender == rhs.gender 126 | && lhs.friends == rhs.friends 127 | } 128 | 129 | enum Color: String, Codable { 130 | case red 131 | case green 132 | case blue 133 | case brown 134 | } 135 | 136 | enum Gender: String, Codable { 137 | case male 138 | case female 139 | } 140 | 141 | struct Friend: Codable, Equatable { 142 | let id: Int 143 | let name: String 144 | 145 | static func ==(lhs: Friend, rhs: Friend) -> Bool { 146 | return lhs.id == rhs.id 147 | && lhs.name == rhs.name 148 | } 149 | } 150 | 151 | private enum CodingKeys: String, CodingKey { 152 | case eyeColor 153 | case gender 154 | case friends 155 | } 156 | 157 | required init(from decoder: Decoder) throws { 158 | let container = try decoder.container(keyedBy: CodingKeys.self) 159 | 160 | eyeColor = try container.decode(Color.self, forKey: .eyeColor) 161 | gender = try container.decode(Gender.self, forKey: .gender) 162 | friends = try container.decode([Friend].self, forKey: .friends) 163 | 164 | // try super.init(from: container.superDecoder()) 165 | try super.init(from: container.superDecoder()) 166 | } 167 | 168 | override init(foundationJSON json: Any) throws { 169 | guard 170 | let json = json as? [String: Any], 171 | let eyeColorRawValue = json["eyeColor"] as? String, 172 | let eyeColor = Color(rawValue: eyeColorRawValue), 173 | let genderRawValue = json["gender"] as? String, 174 | let gender = Gender(rawValue: genderRawValue), 175 | let friendsObjects = json["friends"] as? [Any] 176 | else { throw FoundationJSONError.typeMismatch } 177 | 178 | self.friends = try friendsObjects.map(Friend.init) 179 | self.eyeColor = eyeColor 180 | self.gender = gender 181 | 182 | try super.init(foundationJSON: json) 183 | } 184 | } 185 | 186 | // MARK: - Foundation 187 | 188 | enum FoundationJSONError: Error { 189 | case typeMismatch 190 | } 191 | 192 | extension User.Friend { 193 | 194 | init(foundationJSON json: Any) throws { 195 | guard 196 | let json = json as? [String: Any], 197 | let id = json["id"] as? Int, 198 | let name = json["name"] as? String 199 | else { throw FoundationJSONError.typeMismatch } 200 | self.id = id 201 | self.name = name 202 | } 203 | } 204 | 205 | class TUser: BaseClass, Equatable { 206 | let eyeColor: Color 207 | let gender: Gender 208 | let friends: [Friend] 209 | 210 | static func ==(lhs: TUser, rhs: TUser) -> Bool { 211 | return lhs._id == rhs._id 212 | && lhs.index == rhs.index 213 | && lhs.guid == rhs.guid 214 | && lhs.isActive == rhs.isActive 215 | && lhs.balance == rhs.balance 216 | && lhs.picture == rhs.picture 217 | && lhs.age == rhs.age 218 | && lhs.name == rhs.name 219 | && lhs.company == rhs.company 220 | && lhs.email == rhs.email 221 | && lhs.phone == rhs.phone 222 | && lhs.address == rhs.address 223 | && lhs.about == rhs.about 224 | && lhs.registered == rhs.registered 225 | && lhs.latitude == rhs.latitude 226 | && lhs.longitude == rhs.longitude 227 | && lhs.tags == rhs.tags 228 | && lhs.greeting == rhs.greeting 229 | && lhs.favoriteFruit == rhs.favoriteFruit 230 | && lhs.eyeColor == rhs.eyeColor 231 | && lhs.gender == rhs.gender 232 | && lhs.friends == rhs.friends 233 | } 234 | 235 | enum Color: String, Codable { 236 | case red 237 | case green 238 | case blue 239 | case brown 240 | } 241 | 242 | enum Gender: String, Codable { 243 | case male 244 | case female 245 | } 246 | 247 | struct Friend: Codable, Equatable { 248 | let id: Int 249 | let name: String 250 | 251 | static func ==(lhs: Friend, rhs: Friend) -> Bool { 252 | return lhs.id == rhs.id 253 | && lhs.name == rhs.name 254 | } 255 | 256 | init(foundationJSON json: Any) throws { 257 | guard 258 | let json = json as? [String: Any], 259 | let id = json["id"] as? Int, 260 | let name = json["name"] as? String 261 | else { throw FoundationJSONError.typeMismatch } 262 | self.id = id 263 | self.name = name 264 | } 265 | } 266 | 267 | private enum CodingKeys: String, CodingKey { 268 | case eyeColor 269 | case gender 270 | case friends 271 | } 272 | 273 | required init(from decoder: Decoder) throws { 274 | let container = try decoder.container(keyedBy: CodingKeys.self) 275 | 276 | eyeColor = try container.decode(Color.self, forKey: .eyeColor) 277 | gender = try container.decode(Gender.self, forKey: .gender) 278 | friends = try container.decode([Friend].self, forKey: .friends) 279 | 280 | // try super.init(from: container.superDecoder()) 281 | try super.init(from: decoder) 282 | } 283 | 284 | override init(foundationJSON json: Any) throws { 285 | guard 286 | let json = json as? [String: Any], 287 | let eyeColorRawValue = json["eyeColor"] as? String, 288 | let eyeColor = Color(rawValue: eyeColorRawValue), 289 | let genderRawValue = json["gender"] as? String, 290 | let gender = Gender(rawValue: genderRawValue), 291 | let friendsObjects = json["friends"] as? [Any] 292 | else { throw FoundationJSONError.typeMismatch } 293 | 294 | self.friends = try friendsObjects.map(Friend.init) 295 | self.eyeColor = eyeColor 296 | self.gender = gender 297 | 298 | try super.init(foundationJSON: json) 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /JSONDecoder/Parser/JSONParser.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Parser.swift 3 | // JSONCoder 4 | // 5 | // Created by kemchenj on 20/10/2017. 6 | // Copyright © 2017 kemchenj. All rights reserved. 7 | // 8 | 9 | import struct Foundation.Data 10 | 11 | #if os(Linux) 12 | import func SwiftGlibc.C.math.pow 13 | #else 14 | import func Darwin.C.math.pow 15 | #endif 16 | 17 | final class JSONParser { 18 | 19 | struct Option: OptionSet { 20 | public let rawValue: UInt8 21 | public init (rawValue: UInt8) { self.rawValue = rawValue } 22 | 23 | public static let omitNulls = Option(rawValue: 0b0001) 24 | public static let allowFragments = Option(rawValue: 0b0010) 25 | } 26 | 27 | private let omitNulls: Bool 28 | 29 | private var pointer: UnsafePointer 30 | private var buffer: UnsafeBufferPointer 31 | 32 | private var stringBuffer: [UTF8.CodeUnit] = [] 33 | 34 | private init(bufferPointers: UnsafeBufferPointer, options: Option) throws { 35 | guard let pointer = bufferPointers.baseAddress, pointer != bufferPointers.endAddress else { 36 | throw Error(byteOffset: 0, reason: .emptyStream) 37 | } 38 | 39 | self.buffer = bufferPointers 40 | self.pointer = pointer 41 | self.omitNulls = options.contains(.omitNulls) 42 | } 43 | } 44 | 45 | extension JSONParser { 46 | 47 | static func parse(_ data: Data, options: Option = []) throws -> JSON { 48 | return try data.withUnsafeBytes { pointer in 49 | return try parse(UnsafeBufferPointer(start: pointer, count: data.count), options: options) 50 | } 51 | } 52 | 53 | static func parse(_ data: [UTF8.CodeUnit], options: Option = []) throws -> JSON { 54 | return try data.withUnsafeBufferPointer { buffer in 55 | return try parse(buffer, options: options) 56 | } 57 | } 58 | 59 | static func parse(_ string: String, options: Option = []) throws -> JSON { 60 | return try parse(Array(string.utf8), options: options) 61 | } 62 | 63 | static func parse(_ buffer: UnsafeBufferPointer, options: Option = []) throws -> JSON { 64 | let parser = try JSONParser(bufferPointers: buffer, options: options) 65 | 66 | do { 67 | try parser.skipWhitespace() 68 | 69 | let rootValue = try parser.parseValue() 70 | 71 | if !options.contains(.allowFragments) { 72 | switch rootValue { 73 | case .array, .object: break 74 | default: throw Error.Reason.fragmentedJSON 75 | } 76 | } 77 | 78 | try parser.skipWhitespace() 79 | 80 | guard parser.pointer == parser.buffer.endAddress else { throw Error.Reason.invalidSyntax } 81 | 82 | return rootValue 83 | } catch let error as Error.Reason { 84 | throw Error(byteOffset: parser.buffer.baseAddress!.distance(to: parser.pointer), reason: error) 85 | } 86 | } 87 | } 88 | 89 | private extension JSONParser { 90 | 91 | func peek(aheadBy n: Int = 0) -> UTF8.CodeUnit? { 92 | let shiftedPointer = pointer.advanced(by: n) 93 | guard shiftedPointer < buffer.endAddress else { 94 | return nil 95 | } 96 | return shiftedPointer.pointee 97 | } 98 | 99 | @discardableResult 100 | func pop() -> UTF8.CodeUnit { 101 | assert(pointer != buffer.endAddress) 102 | defer { pointer = pointer.advanced(by: 1) } 103 | return pointer.pointee 104 | } 105 | 106 | func hasPrefix(_ prefix: [UTF8.CodeUnit]) -> Bool { 107 | for (index, byte) in prefix.enumerated() { 108 | guard byte == peek(aheadBy: index) else { return false } 109 | } 110 | return true 111 | } 112 | } 113 | 114 | private extension JSONParser { 115 | 116 | func parseValue() throws -> JSON { 117 | assert(!pointer.pointee.isWhiteSpace) 118 | 119 | defer { _ = try? skipWhitespace() } 120 | 121 | switch peek() { 122 | case objectOpen: 123 | return try parseObject() 124 | case arrayOpen: 125 | return try parseArray() 126 | case quote: 127 | return try .string(parseString()) 128 | case minus, numbers: 129 | return try parseNumber() 130 | case f: 131 | pop() 132 | try assertFollowedBy(alse) 133 | return .bool(false) 134 | case t: 135 | pop() 136 | try assertFollowedBy(rue) 137 | return .bool(true) 138 | case n: 139 | pop() 140 | try assertFollowedBy(ull) 141 | return .null 142 | default: 143 | throw Error.Reason.invalidSyntax 144 | } 145 | } 146 | 147 | func assertFollowedBy(_ chars: [UTF8.CodeUnit]) throws { 148 | try chars.forEach { 149 | guard $0 == pop() else { throw Error.Reason.invalidLiteral } 150 | } 151 | } 152 | 153 | func parseObject() throws -> JSON { 154 | assert(peek() == objectOpen) 155 | pop() 156 | 157 | try skipWhitespace() 158 | 159 | guard peek() != objectClose else { 160 | pop() 161 | return .object([:]) 162 | } 163 | 164 | var tempDict: [String: JSON] = Dictionary.init(minimumCapacity: 6) 165 | var wasComma = false 166 | 167 | repeat { 168 | switch peek() { 169 | case comma: 170 | guard !wasComma else { throw Error.Reason.trailingComma } 171 | 172 | wasComma = true 173 | pop() 174 | try skipWhitespace() 175 | case quote: 176 | if tempDict.count > 0 && !wasComma { 177 | throw Error.Reason.expectedComma 178 | } 179 | 180 | let key = try parseString() 181 | try skipWhitespace() 182 | 183 | guard pop() == colon else { throw Error.Reason.expectedColon } 184 | try skipWhitespace() 185 | 186 | let value = try parseValue() 187 | wasComma = false 188 | 189 | switch value { 190 | case nil where omitNulls: 191 | break 192 | default: 193 | tempDict[key] = value 194 | } 195 | 196 | case objectClose: 197 | guard !wasComma else { throw Error.Reason.trailingComma } 198 | 199 | pop() 200 | return .object(tempDict) 201 | 202 | default: 203 | throw Error.Reason.invalidSyntax 204 | } 205 | } while true 206 | } 207 | 208 | func parseArray() throws -> JSON { 209 | assert(peek() == arrayOpen) 210 | pop() 211 | 212 | try skipWhitespace() 213 | 214 | guard peek() != arrayClose else { 215 | pop() 216 | return .array([]) 217 | } 218 | 219 | var tempArray: [JSON] = [] 220 | tempArray.reserveCapacity(20) 221 | 222 | var wasComma = false 223 | 224 | repeat { 225 | switch peek() { 226 | case comma: 227 | guard !wasComma, !tempArray.isEmpty else { 228 | throw Error.Reason.invalidSyntax 229 | } 230 | 231 | wasComma = true 232 | try skipComma() 233 | case arrayClose: 234 | guard !wasComma else { throw Error.Reason.invalidSyntax } 235 | 236 | _ = pop() 237 | return .array(tempArray) 238 | case nil: 239 | throw Error.Reason.endOfStream 240 | default: 241 | if !wasComma && !tempArray.isEmpty { 242 | throw Error.Reason.expectedComma 243 | } 244 | 245 | let value = try parseValue() 246 | try skipWhitespace() 247 | wasComma = false 248 | 249 | switch value { 250 | case .null where omitNulls: 251 | if peek() == comma { 252 | try skipComma() 253 | wasComma = true 254 | } 255 | default: 256 | tempArray.append(value) 257 | } 258 | } 259 | } while true 260 | } 261 | 262 | func parseNumber() throws -> JSON { 263 | assert(numbers ~= peek()! || minus == peek()) 264 | 265 | var seenExponent = false // 有小数 266 | var seenDecimal = false // 有指数 267 | 268 | let negative: Bool = { 269 | guard minus == peek() else { return false } 270 | pop() 271 | return true 272 | }() 273 | 274 | var significand: UInt64 = 0 275 | var mantisa: UInt64 = 0 276 | var divisor: Double = 1 277 | var exponent: UInt64 = 0 278 | var negativeExponent = false 279 | var didOverflow = false 280 | 281 | repeat { 282 | switch peek() { 283 | // 数字(没有小数,也没有小数点) 284 | case numbers where !seenDecimal && !seenExponent: 285 | // 将原本的数字向左挪一位 286 | // 0183 -> 1830 287 | (significand, didOverflow) = significand.multipliedReportingOverflow(by: 10) 288 | guard !didOverflow else { throw Error.Reason.numberOverflow } 289 | 290 | // 把新的数字放到个位上 291 | // 1830 + 7 -> 1837 292 | (significand, didOverflow) = significand.addingReportingOverflow(UInt64(pop() - zero)) 293 | guard !didOverflow else { throw Error.Reason.numberOverflow } 294 | 295 | // 数字(有小数的) 296 | case numbers where seenDecimal && !seenExponent : 297 | // 用 divisor 表示小数点的位置 298 | divisor *= 10 299 | 300 | // 有了 divisor,就可以先用整数来表示当前的数字 301 | (mantisa, didOverflow) = mantisa.multipliedReportingOverflow(by: 10) 302 | guard !didOverflow else { throw Error.Reason.numberOverflow } 303 | 304 | (mantisa, didOverflow) = mantisa.addingReportingOverflow(UInt64(pop() - zero)) 305 | guard !didOverflow else { throw Error.Reason.numberOverflow } 306 | 307 | // 数字(带指数的) 308 | case numbers where seenExponent: 309 | (exponent, didOverflow) = exponent.multipliedReportingOverflow(by: 10) 310 | guard !didOverflow else { throw Error.Reason.numberOverflow } 311 | 312 | (exponent, didOverflow) = exponent.addingReportingOverflow(UInt64(pop() - zero)) 313 | guard !didOverflow else { throw Error.Reason.numberOverflow } 314 | 315 | // 小数点 316 | case decimal where !seenExponent && !seenDecimal: 317 | pop() 318 | seenDecimal = true 319 | guard let next = peek(), numbers ~= next else { throw Error.Reason.invalidNumber } 320 | 321 | // E 或者 e 322 | case E? where !seenExponent, e? where !seenExponent: 323 | pop() 324 | seenExponent = true 325 | 326 | if peek() == minus { 327 | negativeExponent = true 328 | pop() 329 | } else if peek() == plus { 330 | pop() 331 | } 332 | 333 | guard let next = peek(), numbers ~= next else { throw Error.Reason.invalidNumber } 334 | 335 | // 终止符 336 | case let value? where value.isTerminator: 337 | fallthrough 338 | 339 | case nil: 340 | return try constructNumber( 341 | significand : significand, 342 | mantisa : seenDecimal ? mantisa : nil, 343 | exponent : seenExponent ? exponent : nil, 344 | divisor : divisor, 345 | negative : negative, 346 | negativeExponent: negativeExponent) 347 | 348 | default: 349 | throw Error.Reason.invalidNumber 350 | } 351 | } while true 352 | } 353 | 354 | func constructNumber(significand: UInt64, mantisa: UInt64?, exponent: UInt64?, divisor: Double, negative: Bool, negativeExponent: Bool) throws -> JSON { 355 | // 没有尾数或者没有指数 356 | if mantisa != nil || exponent != nil { 357 | let number = Double(negative ? -1 : 1) * (Double(significand) + Double(mantisa ?? 0) / divisor) 358 | 359 | // 有指数的时候计算完再乘上去 360 | if let exponent = exponent { 361 | let doubleExponent = Double(exponent) 362 | return .double(Double(number) * pow(10, negativeExponent ? -doubleExponent : doubleExponent)) 363 | } else { 364 | return .double(number) 365 | } 366 | } else { 367 | switch significand { 368 | // 正数 369 | case validUnsigned64BitInteger where !negative: 370 | return .integer(Int64(significand)) 371 | // 负数且超出了 Int64 的范围 372 | case UInt64(Int64.max) + 1 where negative: 373 | return .integer(Int64.min) 374 | // 负数 375 | case validUnsigned64BitInteger where negative: 376 | return .integer(-Int64(significand)) 377 | default: 378 | throw Error.Reason.numberOverflow 379 | } 380 | } 381 | } 382 | 383 | func parseString() throws -> String { 384 | assert(peek() == quote) 385 | pop() 386 | 387 | var isEscaped = false 388 | stringBuffer.removeAll(keepingCapacity: true) 389 | 390 | repeat { 391 | guard let codeUnit = peek() else { throw Error.Reason.invalidEscape } 392 | 393 | pop() 394 | 395 | // 字符为 \\ 且非转义,进入转义模式 396 | if codeUnit == backslash && !isEscaped { 397 | isEscaped = true 398 | // 字符为 " 且非转义 399 | } else if codeUnit == quote && !isEscaped { 400 | stringBuffer.append(0) 401 | return stringBuffer.withUnsafeBufferPointer { bufferPoint in 402 | return String(cString: unsafeBitCast(bufferPoint.baseAddress, to: UnsafePointer.self)) 403 | } 404 | // 转义 405 | } else if isEscaped { 406 | switch codeUnit { 407 | case r : stringBuffer.append(cr) 408 | case t : stringBuffer.append(tab) 409 | case n : stringBuffer.append(newline) 410 | case b : stringBuffer.append(backspace) 411 | case f : stringBuffer.append(formfeed) 412 | case quote : stringBuffer.append(quote) 413 | case slash : stringBuffer.append(slash) 414 | case backslash : stringBuffer.append(backslash) 415 | case u : UTF8.encode(try parseUnicodeScalar(), into: { stringBuffer.append($0) }) 416 | default : throw Error.Reason.invalidUnicode 417 | } 418 | 419 | isEscaped = false 420 | // 排除掉非法的 Unicode 字符和非法的字符 421 | } else if invalidUnicodeBytes.contains(codeUnit) || codeUnit == 0xC0 || codeUnit == 0xC1 { 422 | throw Error.Reason.invalidUnicode 423 | } else { 424 | stringBuffer.append(codeUnit) 425 | } 426 | } while true 427 | } 428 | } 429 | 430 | private extension JSONParser { 431 | 432 | func parseUnicodeEscape() throws -> UTF16.CodeUnit { 433 | // 总共十六位,分成四次完成,每次编码四位 434 | // 0b 0000 0000 0000 0000 435 | return try (0..<4).reduce(0b0000000000000000) { (codeUnit, _) in 436 | var newCodeUnit = codeUnit << 4 437 | 438 | // 下面是一些 Magic Number,可以将 UTF8 转为 Unicode 439 | let code = pop() 440 | switch code { 441 | case numbers : newCodeUnit += UInt16(code - 48) 442 | case alphaNumericLower : newCodeUnit += UInt16(code - 87) 443 | case alphaNumericUpper : newCodeUnit += UInt16(code - 55) 444 | default : throw Error.Reason.invalidEscape 445 | } 446 | 447 | return newCodeUnit 448 | } 449 | } 450 | 451 | func parseUnicodeScalar() throws -> UnicodeScalar { 452 | var buffer: [UTF16.CodeUnit] = [] 453 | 454 | // 构造 UTF 16 的第一个字节 455 | let codeUnit = try parseUnicodeEscape() 456 | buffer.append(codeUnit) 457 | 458 | // http://blog.csdn.net/shuilan0066/article/details/7865715 459 | // 查看 UTF16 是否有辅助位 460 | if UTF16.isLeadSurrogate(codeUnit) { 461 | // 将 UTF 16 的第二个字节也构造出来 462 | guard pop() == backslash && pop() == u else { throw Error.Reason.invalidUnicode } 463 | let trailingSurrogate = try parseUnicodeEscape() 464 | guard UTF16.isTrailSurrogate(trailingSurrogate) else { throw Error.Reason.invalidUnicode } 465 | buffer.append(trailingSurrogate) 466 | } 467 | 468 | var gen = buffer.makeIterator() 469 | 470 | var utf = UTF16() 471 | 472 | switch utf.decode(&gen) { 473 | case .scalarValue(let scalar) : return scalar 474 | case .emptyInput, .error : throw Error.Reason.invalidUnicode 475 | } 476 | } 477 | 478 | func skipComma() throws { 479 | assert(peek() == comma) 480 | pop() 481 | try skipWhitespace() 482 | } 483 | 484 | func skipWhitespace() throws { 485 | func skipComments() throws -> Bool { 486 | if hasPrefix(lineComment) { 487 | while let char = peek(), char != newline { 488 | pop() 489 | } 490 | 491 | return true 492 | } else if hasPrefix(blockCommentStart) { 493 | pop() // '/' 494 | pop() // '*' 495 | defer { pop() } // '/' 496 | 497 | // 处理多个注释嵌套的问题 498 | var depth: UInt = 1 499 | repeat { 500 | guard let _ = peek() else { 501 | throw Error.Reason.unmatchedComment 502 | } 503 | 504 | if hasPrefix(blockCommentStart) { 505 | depth += 1 506 | } else if hasPrefix(blockCommentEnd) { 507 | depth -= 1 508 | } 509 | 510 | pop() 511 | } while depth > 0 512 | 513 | return true 514 | } 515 | 516 | return false 517 | } 518 | 519 | while pointer != buffer.endAddress && pointer.pointee.isWhiteSpace { 520 | pop() 521 | } 522 | } 523 | } 524 | 525 | fileprivate extension UnsafeBufferPointer { 526 | 527 | var endAddress: UnsafePointer { 528 | return baseAddress!.advanced(by: endIndex) 529 | } 530 | } 531 | 532 | fileprivate extension UTF8.CodeUnit { 533 | 534 | var isWhiteSpace: Bool { 535 | return [space, tab, cr, newline, formfeed].contains(self) 536 | } 537 | 538 | var isTerminator: Bool { 539 | return [space, comma, objectClose, arrayClose].contains(self) 540 | } 541 | } 542 | -------------------------------------------------------------------------------- /JSONDecoder.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | C800561E1F9F282C00617159 /* JSONError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C800561D1F9F282C00617159 /* JSONError.swift */; }; 11 | C80056201F9F3CA100617159 /* Helper.swift in Sources */ = {isa = PBXBuildFile; fileRef = C800561F1F9F3CA100617159 /* Helper.swift */; }; 12 | C80DACAC1FB8842600F4472E /* JSON+Literal.swift in Sources */ = {isa = PBXBuildFile; fileRef = C80DACAB1FB8842600F4472E /* JSON+Literal.swift */; }; 13 | C81E9A061FAEFAAC00055DDB /* JSONKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81E9A051FAEFAAC00055DDB /* JSONKey.swift */; }; 14 | C81E9A0A1FAF50F800055DDB /* large.json in Resources */ = {isa = PBXBuildFile; fileRef = C81E9A091FAF50F800055DDB /* large.json */; }; 15 | C81E9A0C1FAF51EC00055DDB /* UserModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C81E9A0B1FAF51EC00055DDB /* UserModel.swift */; }; 16 | C85CC3461FA8C8DB00DBF75A /* JSONDecoder+Decoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C85CC3451FA8C8DB00DBF75A /* JSONDecoder+Decoder.swift */; }; 17 | C85CC3481FA8C8E900DBF75A /* JSONDecoder+UnkeydContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C85CC3471FA8C8E900DBF75A /* JSONDecoder+UnkeydContainer.swift */; }; 18 | C85CC34A1FA8C8FA00DBF75A /* JSONDecoder+KeyedContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C85CC3491FA8C8FA00DBF75A /* JSONDecoder+KeyedContainer.swift */; }; 19 | C85CC34C1FA8C90700DBF75A /* JSONDecoder+SingleValueContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = C85CC34B1FA8C90700DBF75A /* JSONDecoder+SingleValueContainer.swift */; }; 20 | C8A0B82C1F996D00007BB700 /* JSONDecoder.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C8A0B8221F996D00007BB700 /* JSONDecoder.framework */; }; 21 | C8A0B8311F996D00007BB700 /* JSONCoderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8A0B8301F996D00007BB700 /* JSONCoderTests.swift */; }; 22 | C8A0B8331F996D00007BB700 /* JSONCoder.h in Headers */ = {isa = PBXBuildFile; fileRef = C8A0B8251F996D00007BB700 /* JSONCoder.h */; settings = {ATTRIBUTES = (Public, ); }; }; 23 | C8DDB9A21F9DF4AB002B7C39 /* JSONParseError.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DDB9A11F9DF4AB002B7C39 /* JSONParseError.swift */; }; 24 | C8DE45D91FA182F200FC6348 /* PatternMatch.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DE45D81FA182F200FC6348 /* PatternMatch.swift */; }; 25 | C8DE45DE1FA1CEB600FC6348 /* JSONDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8DE45DD1FA1CEB600FC6348 /* JSONDecoder.swift */; }; 26 | C8E25E1C1F99FF0E0014472E /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E25E1B1F99FF0E0014472E /* JSON.swift */; }; 27 | C8E25E201F9A00990014472E /* JSONParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = C8E25E1F1F9A00990014472E /* JSONParser.swift */; }; 28 | /* End PBXBuildFile section */ 29 | 30 | /* Begin PBXContainerItemProxy section */ 31 | C8A0B82D1F996D00007BB700 /* PBXContainerItemProxy */ = { 32 | isa = PBXContainerItemProxy; 33 | containerPortal = C8A0B8191F996CFF007BB700 /* Project object */; 34 | proxyType = 1; 35 | remoteGlobalIDString = C8A0B8211F996CFF007BB700; 36 | remoteInfo = JSONCoder; 37 | }; 38 | /* End PBXContainerItemProxy section */ 39 | 40 | /* Begin PBXFileReference section */ 41 | C800561D1F9F282C00617159 /* JSONError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONError.swift; sourceTree = ""; }; 42 | C800561F1F9F3CA100617159 /* Helper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helper.swift; sourceTree = ""; }; 43 | C80DACAB1FB8842600F4472E /* JSON+Literal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSON+Literal.swift"; sourceTree = ""; }; 44 | C81E9A051FAEFAAC00055DDB /* JSONKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONKey.swift; sourceTree = ""; }; 45 | C81E9A091FAF50F800055DDB /* large.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = large.json; sourceTree = ""; }; 46 | C81E9A0B1FAF51EC00055DDB /* UserModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserModel.swift; sourceTree = ""; }; 47 | C85CC3451FA8C8DB00DBF75A /* JSONDecoder+Decoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+Decoder.swift"; sourceTree = ""; }; 48 | C85CC3471FA8C8E900DBF75A /* JSONDecoder+UnkeydContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+UnkeydContainer.swift"; sourceTree = ""; }; 49 | C85CC3491FA8C8FA00DBF75A /* JSONDecoder+KeyedContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+KeyedContainer.swift"; sourceTree = ""; }; 50 | C85CC34B1FA8C90700DBF75A /* JSONDecoder+SingleValueContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+SingleValueContainer.swift"; sourceTree = ""; }; 51 | C8A0B8221F996D00007BB700 /* JSONDecoder.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = JSONDecoder.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 52 | C8A0B8251F996D00007BB700 /* JSONCoder.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSONCoder.h; sourceTree = ""; }; 53 | C8A0B8261F996D00007BB700 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 54 | C8A0B82B1F996D00007BB700 /* JSONDecoderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = JSONDecoderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | C8A0B8301F996D00007BB700 /* JSONCoderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONCoderTests.swift; sourceTree = ""; }; 56 | C8A0B8321F996D00007BB700 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 57 | C8DDB9A11F9DF4AB002B7C39 /* JSONParseError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONParseError.swift; sourceTree = ""; }; 58 | C8DE45D81FA182F200FC6348 /* PatternMatch.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PatternMatch.swift; sourceTree = ""; }; 59 | C8DE45DD1FA1CEB600FC6348 /* JSONDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONDecoder.swift; sourceTree = ""; }; 60 | C8E25E1B1F99FF0E0014472E /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = ""; }; 61 | C8E25E1F1F9A00990014472E /* JSONParser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONParser.swift; sourceTree = ""; }; 62 | /* End PBXFileReference section */ 63 | 64 | /* Begin PBXFrameworksBuildPhase section */ 65 | C8A0B81E1F996CFF007BB700 /* Frameworks */ = { 66 | isa = PBXFrameworksBuildPhase; 67 | buildActionMask = 2147483647; 68 | files = ( 69 | ); 70 | runOnlyForDeploymentPostprocessing = 0; 71 | }; 72 | C8A0B8281F996D00007BB700 /* Frameworks */ = { 73 | isa = PBXFrameworksBuildPhase; 74 | buildActionMask = 2147483647; 75 | files = ( 76 | C8A0B82C1F996D00007BB700 /* JSONDecoder.framework in Frameworks */, 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | C8A0B8181F996CFF007BB700 = { 84 | isa = PBXGroup; 85 | children = ( 86 | C8A0B8241F996D00007BB700 /* JSONDecoder */, 87 | C8A0B82F1F996D00007BB700 /* JSONDecoderTests */, 88 | C8A0B8231F996D00007BB700 /* Products */, 89 | ); 90 | sourceTree = ""; 91 | }; 92 | C8A0B8231F996D00007BB700 /* Products */ = { 93 | isa = PBXGroup; 94 | children = ( 95 | C8A0B8221F996D00007BB700 /* JSONDecoder.framework */, 96 | C8A0B82B1F996D00007BB700 /* JSONDecoderTests.xctest */, 97 | ); 98 | name = Products; 99 | sourceTree = ""; 100 | }; 101 | C8A0B8241F996D00007BB700 /* JSONDecoder */ = { 102 | isa = PBXGroup; 103 | children = ( 104 | C8E25E171F99FE1B0014472E /* Model */, 105 | C8E25E181F99FE410014472E /* Parser */, 106 | C8DE45DC1FA1CE9400FC6348 /* Coder */, 107 | C8A0B8251F996D00007BB700 /* JSONCoder.h */, 108 | C8A0B8261F996D00007BB700 /* Info.plist */, 109 | ); 110 | path = JSONDecoder; 111 | sourceTree = ""; 112 | }; 113 | C8A0B82F1F996D00007BB700 /* JSONDecoderTests */ = { 114 | isa = PBXGroup; 115 | children = ( 116 | C8A0B8301F996D00007BB700 /* JSONCoderTests.swift */, 117 | C81E9A091FAF50F800055DDB /* large.json */, 118 | C81E9A0B1FAF51EC00055DDB /* UserModel.swift */, 119 | C8A0B8321F996D00007BB700 /* Info.plist */, 120 | ); 121 | path = JSONDecoderTests; 122 | sourceTree = ""; 123 | }; 124 | C8DE45DC1FA1CE9400FC6348 /* Coder */ = { 125 | isa = PBXGroup; 126 | children = ( 127 | C8DE45DD1FA1CEB600FC6348 /* JSONDecoder.swift */, 128 | C85CC3451FA8C8DB00DBF75A /* JSONDecoder+Decoder.swift */, 129 | C85CC3471FA8C8E900DBF75A /* JSONDecoder+UnkeydContainer.swift */, 130 | C85CC3491FA8C8FA00DBF75A /* JSONDecoder+KeyedContainer.swift */, 131 | C85CC34B1FA8C90700DBF75A /* JSONDecoder+SingleValueContainer.swift */, 132 | C81E9A051FAEFAAC00055DDB /* JSONKey.swift */, 133 | ); 134 | path = Coder; 135 | sourceTree = ""; 136 | }; 137 | C8E25E171F99FE1B0014472E /* Model */ = { 138 | isa = PBXGroup; 139 | children = ( 140 | C8E25E1B1F99FF0E0014472E /* JSON.swift */, 141 | C800561D1F9F282C00617159 /* JSONError.swift */, 142 | C80DACAB1FB8842600F4472E /* JSON+Literal.swift */, 143 | ); 144 | path = Model; 145 | sourceTree = ""; 146 | }; 147 | C8E25E181F99FE410014472E /* Parser */ = { 148 | isa = PBXGroup; 149 | children = ( 150 | C8E25E1F1F9A00990014472E /* JSONParser.swift */, 151 | C800561F1F9F3CA100617159 /* Helper.swift */, 152 | C8DDB9A11F9DF4AB002B7C39 /* JSONParseError.swift */, 153 | C8DE45D81FA182F200FC6348 /* PatternMatch.swift */, 154 | ); 155 | path = Parser; 156 | sourceTree = ""; 157 | }; 158 | /* End PBXGroup section */ 159 | 160 | /* Begin PBXHeadersBuildPhase section */ 161 | C8A0B81F1F996CFF007BB700 /* Headers */ = { 162 | isa = PBXHeadersBuildPhase; 163 | buildActionMask = 2147483647; 164 | files = ( 165 | C8A0B8331F996D00007BB700 /* JSONCoder.h in Headers */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXHeadersBuildPhase section */ 170 | 171 | /* Begin PBXNativeTarget section */ 172 | C8A0B8211F996CFF007BB700 /* JSONDecoder */ = { 173 | isa = PBXNativeTarget; 174 | buildConfigurationList = C8A0B8361F996D00007BB700 /* Build configuration list for PBXNativeTarget "JSONDecoder" */; 175 | buildPhases = ( 176 | C8A0B81D1F996CFF007BB700 /* Sources */, 177 | C8A0B81E1F996CFF007BB700 /* Frameworks */, 178 | C8A0B81F1F996CFF007BB700 /* Headers */, 179 | C8A0B8201F996CFF007BB700 /* Resources */, 180 | ); 181 | buildRules = ( 182 | ); 183 | dependencies = ( 184 | ); 185 | name = JSONDecoder; 186 | productName = JSONCoder; 187 | productReference = C8A0B8221F996D00007BB700 /* JSONDecoder.framework */; 188 | productType = "com.apple.product-type.framework"; 189 | }; 190 | C8A0B82A1F996D00007BB700 /* JSONDecoderTests */ = { 191 | isa = PBXNativeTarget; 192 | buildConfigurationList = C8A0B8391F996D00007BB700 /* Build configuration list for PBXNativeTarget "JSONDecoderTests" */; 193 | buildPhases = ( 194 | C8A0B8271F996D00007BB700 /* Sources */, 195 | C8A0B8281F996D00007BB700 /* Frameworks */, 196 | C8A0B8291F996D00007BB700 /* Resources */, 197 | ); 198 | buildRules = ( 199 | ); 200 | dependencies = ( 201 | C8A0B82E1F996D00007BB700 /* PBXTargetDependency */, 202 | ); 203 | name = JSONDecoderTests; 204 | productName = JSONCoderTests; 205 | productReference = C8A0B82B1F996D00007BB700 /* JSONDecoderTests.xctest */; 206 | productType = "com.apple.product-type.bundle.unit-test"; 207 | }; 208 | /* End PBXNativeTarget section */ 209 | 210 | /* Begin PBXProject section */ 211 | C8A0B8191F996CFF007BB700 /* Project object */ = { 212 | isa = PBXProject; 213 | attributes = { 214 | LastSwiftUpdateCheck = 0900; 215 | LastUpgradeCheck = 0930; 216 | ORGANIZATIONNAME = kemchenj; 217 | TargetAttributes = { 218 | C8A0B8211F996CFF007BB700 = { 219 | CreatedOnToolsVersion = 9.0.1; 220 | LastSwiftMigration = 0900; 221 | ProvisioningStyle = Automatic; 222 | }; 223 | C8A0B82A1F996D00007BB700 = { 224 | CreatedOnToolsVersion = 9.0.1; 225 | ProvisioningStyle = Automatic; 226 | }; 227 | }; 228 | }; 229 | buildConfigurationList = C8A0B81C1F996CFF007BB700 /* Build configuration list for PBXProject "JSONDecoder" */; 230 | compatibilityVersion = "Xcode 9.3"; 231 | developmentRegion = en; 232 | hasScannedForEncodings = 0; 233 | knownRegions = ( 234 | en, 235 | ); 236 | mainGroup = C8A0B8181F996CFF007BB700; 237 | productRefGroup = C8A0B8231F996D00007BB700 /* Products */; 238 | projectDirPath = ""; 239 | projectRoot = ""; 240 | targets = ( 241 | C8A0B8211F996CFF007BB700 /* JSONDecoder */, 242 | C8A0B82A1F996D00007BB700 /* JSONDecoderTests */, 243 | ); 244 | }; 245 | /* End PBXProject section */ 246 | 247 | /* Begin PBXResourcesBuildPhase section */ 248 | C8A0B8201F996CFF007BB700 /* Resources */ = { 249 | isa = PBXResourcesBuildPhase; 250 | buildActionMask = 2147483647; 251 | files = ( 252 | ); 253 | runOnlyForDeploymentPostprocessing = 0; 254 | }; 255 | C8A0B8291F996D00007BB700 /* Resources */ = { 256 | isa = PBXResourcesBuildPhase; 257 | buildActionMask = 2147483647; 258 | files = ( 259 | C81E9A0A1FAF50F800055DDB /* large.json in Resources */, 260 | ); 261 | runOnlyForDeploymentPostprocessing = 0; 262 | }; 263 | /* End PBXResourcesBuildPhase section */ 264 | 265 | /* Begin PBXSourcesBuildPhase section */ 266 | C8A0B81D1F996CFF007BB700 /* Sources */ = { 267 | isa = PBXSourcesBuildPhase; 268 | buildActionMask = 2147483647; 269 | files = ( 270 | C85CC34C1FA8C90700DBF75A /* JSONDecoder+SingleValueContainer.swift in Sources */, 271 | C8DE45D91FA182F200FC6348 /* PatternMatch.swift in Sources */, 272 | C85CC3461FA8C8DB00DBF75A /* JSONDecoder+Decoder.swift in Sources */, 273 | C8E25E201F9A00990014472E /* JSONParser.swift in Sources */, 274 | C81E9A061FAEFAAC00055DDB /* JSONKey.swift in Sources */, 275 | C80056201F9F3CA100617159 /* Helper.swift in Sources */, 276 | C8DE45DE1FA1CEB600FC6348 /* JSONDecoder.swift in Sources */, 277 | C80DACAC1FB8842600F4472E /* JSON+Literal.swift in Sources */, 278 | C85CC34A1FA8C8FA00DBF75A /* JSONDecoder+KeyedContainer.swift in Sources */, 279 | C8E25E1C1F99FF0E0014472E /* JSON.swift in Sources */, 280 | C800561E1F9F282C00617159 /* JSONError.swift in Sources */, 281 | C8DDB9A21F9DF4AB002B7C39 /* JSONParseError.swift in Sources */, 282 | C85CC3481FA8C8E900DBF75A /* JSONDecoder+UnkeydContainer.swift in Sources */, 283 | ); 284 | runOnlyForDeploymentPostprocessing = 0; 285 | }; 286 | C8A0B8271F996D00007BB700 /* Sources */ = { 287 | isa = PBXSourcesBuildPhase; 288 | buildActionMask = 2147483647; 289 | files = ( 290 | C81E9A0C1FAF51EC00055DDB /* UserModel.swift in Sources */, 291 | C8A0B8311F996D00007BB700 /* JSONCoderTests.swift in Sources */, 292 | ); 293 | runOnlyForDeploymentPostprocessing = 0; 294 | }; 295 | /* End PBXSourcesBuildPhase section */ 296 | 297 | /* Begin PBXTargetDependency section */ 298 | C8A0B82E1F996D00007BB700 /* PBXTargetDependency */ = { 299 | isa = PBXTargetDependency; 300 | target = C8A0B8211F996CFF007BB700 /* JSONDecoder */; 301 | targetProxy = C8A0B82D1F996D00007BB700 /* PBXContainerItemProxy */; 302 | }; 303 | /* End PBXTargetDependency section */ 304 | 305 | /* Begin XCBuildConfiguration section */ 306 | C8A0B8341F996D00007BB700 /* Debug */ = { 307 | isa = XCBuildConfiguration; 308 | buildSettings = { 309 | ALWAYS_SEARCH_USER_PATHS = NO; 310 | CLANG_ANALYZER_NONNULL = YES; 311 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 313 | CLANG_CXX_LIBRARY = "libc++"; 314 | CLANG_ENABLE_MODULES = YES; 315 | CLANG_ENABLE_OBJC_ARC = YES; 316 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 317 | CLANG_WARN_BOOL_CONVERSION = YES; 318 | CLANG_WARN_COMMA = YES; 319 | CLANG_WARN_CONSTANT_CONVERSION = YES; 320 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 321 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 322 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 323 | CLANG_WARN_EMPTY_BODY = YES; 324 | CLANG_WARN_ENUM_CONVERSION = YES; 325 | CLANG_WARN_INFINITE_RECURSION = YES; 326 | CLANG_WARN_INT_CONVERSION = YES; 327 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 328 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 329 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 330 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 331 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 332 | CLANG_WARN_STRICT_PROTOTYPES = YES; 333 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 334 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | CODE_SIGN_IDENTITY = "Mac Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | CURRENT_PROJECT_VERSION = 1; 340 | DEBUG_INFORMATION_FORMAT = dwarf; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | ENABLE_TESTABILITY = YES; 343 | GCC_C_LANGUAGE_STANDARD = gnu11; 344 | GCC_DYNAMIC_NO_PIC = NO; 345 | GCC_NO_COMMON_BLOCKS = YES; 346 | GCC_OPTIMIZATION_LEVEL = 0; 347 | GCC_PREPROCESSOR_DEFINITIONS = ( 348 | "DEBUG=1", 349 | "$(inherited)", 350 | ); 351 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 352 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 353 | GCC_WARN_UNDECLARED_SELECTOR = YES; 354 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 355 | GCC_WARN_UNUSED_FUNCTION = YES; 356 | GCC_WARN_UNUSED_VARIABLE = YES; 357 | MACOSX_DEPLOYMENT_TARGET = 10.13; 358 | MTL_ENABLE_DEBUG_INFO = YES; 359 | ONLY_ACTIVE_ARCH = YES; 360 | SDKROOT = macosx; 361 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 362 | SWIFT_COMPILATION_MODE = singlefile; 363 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 364 | VERSIONING_SYSTEM = "apple-generic"; 365 | VERSION_INFO_PREFIX = ""; 366 | }; 367 | name = Debug; 368 | }; 369 | C8A0B8351F996D00007BB700 /* Release */ = { 370 | isa = XCBuildConfiguration; 371 | buildSettings = { 372 | ALWAYS_SEARCH_USER_PATHS = NO; 373 | CLANG_ANALYZER_NONNULL = YES; 374 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 375 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 376 | CLANG_CXX_LIBRARY = "libc++"; 377 | CLANG_ENABLE_MODULES = YES; 378 | CLANG_ENABLE_OBJC_ARC = YES; 379 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 380 | CLANG_WARN_BOOL_CONVERSION = YES; 381 | CLANG_WARN_COMMA = YES; 382 | CLANG_WARN_CONSTANT_CONVERSION = YES; 383 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 384 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 385 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 386 | CLANG_WARN_EMPTY_BODY = YES; 387 | CLANG_WARN_ENUM_CONVERSION = YES; 388 | CLANG_WARN_INFINITE_RECURSION = YES; 389 | CLANG_WARN_INT_CONVERSION = YES; 390 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 391 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 392 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 394 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 395 | CLANG_WARN_STRICT_PROTOTYPES = YES; 396 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 397 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 398 | CLANG_WARN_UNREACHABLE_CODE = YES; 399 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 400 | CODE_SIGN_IDENTITY = "Mac Developer"; 401 | COPY_PHASE_STRIP = NO; 402 | CURRENT_PROJECT_VERSION = 1; 403 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 404 | ENABLE_NS_ASSERTIONS = NO; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | GCC_C_LANGUAGE_STANDARD = gnu11; 407 | GCC_NO_COMMON_BLOCKS = YES; 408 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 409 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 410 | GCC_WARN_UNDECLARED_SELECTOR = YES; 411 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 412 | GCC_WARN_UNUSED_FUNCTION = YES; 413 | GCC_WARN_UNUSED_VARIABLE = YES; 414 | MACOSX_DEPLOYMENT_TARGET = 10.13; 415 | MTL_ENABLE_DEBUG_INFO = NO; 416 | SDKROOT = macosx; 417 | SWIFT_COMPILATION_MODE = wholemodule; 418 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 419 | VERSIONING_SYSTEM = "apple-generic"; 420 | VERSION_INFO_PREFIX = ""; 421 | }; 422 | name = Release; 423 | }; 424 | C8A0B8371F996D00007BB700 /* Debug */ = { 425 | isa = XCBuildConfiguration; 426 | buildSettings = { 427 | APPLICATION_EXTENSION_API_ONLY = YES; 428 | CLANG_ENABLE_MODULES = YES; 429 | CODE_SIGN_IDENTITY = ""; 430 | CODE_SIGN_STYLE = Automatic; 431 | COMBINE_HIDPI_IMAGES = YES; 432 | DEFINES_MODULE = YES; 433 | DEVELOPMENT_TEAM = T28QC3M4TY; 434 | DYLIB_COMPATIBILITY_VERSION = 1; 435 | DYLIB_CURRENT_VERSION = 1; 436 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 437 | FRAMEWORK_VERSION = A; 438 | INFOPLIST_FILE = JSONDecoder/Info.plist; 439 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 440 | LD_RUNPATH_SEARCH_PATHS = ( 441 | "$(inherited)", 442 | "@executable_path/../Frameworks", 443 | "@loader_path/Frameworks", 444 | ); 445 | PRODUCT_BUNDLE_IDENTIFIER = com.kemchenj.JSONCoder; 446 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 447 | SKIP_INSTALL = YES; 448 | SWIFT_COMPILATION_MODE = singlefile; 449 | SWIFT_ENFORCE_EXCLUSIVE_ACCESS = none; 450 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 451 | SWIFT_VERSION = 4.0; 452 | }; 453 | name = Debug; 454 | }; 455 | C8A0B8381F996D00007BB700 /* Release */ = { 456 | isa = XCBuildConfiguration; 457 | buildSettings = { 458 | APPLICATION_EXTENSION_API_ONLY = YES; 459 | CLANG_ENABLE_MODULES = YES; 460 | CODE_SIGN_IDENTITY = ""; 461 | CODE_SIGN_STYLE = Automatic; 462 | COMBINE_HIDPI_IMAGES = YES; 463 | DEFINES_MODULE = YES; 464 | DEVELOPMENT_TEAM = T28QC3M4TY; 465 | DYLIB_COMPATIBILITY_VERSION = 1; 466 | DYLIB_CURRENT_VERSION = 1; 467 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 468 | FRAMEWORK_VERSION = A; 469 | INFOPLIST_FILE = JSONDecoder/Info.plist; 470 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 471 | LD_RUNPATH_SEARCH_PATHS = ( 472 | "$(inherited)", 473 | "@executable_path/../Frameworks", 474 | "@loader_path/Frameworks", 475 | ); 476 | PRODUCT_BUNDLE_IDENTIFIER = com.kemchenj.JSONCoder; 477 | PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; 478 | SKIP_INSTALL = YES; 479 | SWIFT_ENFORCE_EXCLUSIVE_ACCESS = none; 480 | SWIFT_VERSION = 4.0; 481 | }; 482 | name = Release; 483 | }; 484 | C8A0B83A1F996D00007BB700 /* Debug */ = { 485 | isa = XCBuildConfiguration; 486 | buildSettings = { 487 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 488 | CODE_SIGN_IDENTITY = "Mac Developer"; 489 | CODE_SIGN_STYLE = Automatic; 490 | COMBINE_HIDPI_IMAGES = YES; 491 | DEVELOPMENT_TEAM = T28QC3M4TY; 492 | INFOPLIST_FILE = JSONDecoderTests/Info.plist; 493 | LD_RUNPATH_SEARCH_PATHS = ( 494 | "$(inherited)", 495 | "@executable_path/../Frameworks", 496 | "@loader_path/../Frameworks", 497 | ); 498 | PRODUCT_BUNDLE_IDENTIFIER = com.kemchenj.JSONDecoderTests; 499 | PRODUCT_NAME = "$(TARGET_NAME)"; 500 | PROVISIONING_PROFILE_SPECIFIER = ""; 501 | SWIFT_VERSION = 4.0; 502 | }; 503 | name = Debug; 504 | }; 505 | C8A0B83B1F996D00007BB700 /* Release */ = { 506 | isa = XCBuildConfiguration; 507 | buildSettings = { 508 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 509 | CODE_SIGN_IDENTITY = "Mac Developer"; 510 | CODE_SIGN_STYLE = Automatic; 511 | COMBINE_HIDPI_IMAGES = YES; 512 | DEVELOPMENT_TEAM = T28QC3M4TY; 513 | INFOPLIST_FILE = JSONDecoderTests/Info.plist; 514 | LD_RUNPATH_SEARCH_PATHS = ( 515 | "$(inherited)", 516 | "@executable_path/../Frameworks", 517 | "@loader_path/../Frameworks", 518 | ); 519 | PRODUCT_BUNDLE_IDENTIFIER = com.kemchenj.JSONDecoderTests; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | PROVISIONING_PROFILE_SPECIFIER = ""; 522 | SWIFT_VERSION = 4.0; 523 | }; 524 | name = Release; 525 | }; 526 | /* End XCBuildConfiguration section */ 527 | 528 | /* Begin XCConfigurationList section */ 529 | C8A0B81C1F996CFF007BB700 /* Build configuration list for PBXProject "JSONDecoder" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | C8A0B8341F996D00007BB700 /* Debug */, 533 | C8A0B8351F996D00007BB700 /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | C8A0B8361F996D00007BB700 /* Build configuration list for PBXNativeTarget "JSONDecoder" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | C8A0B8371F996D00007BB700 /* Debug */, 542 | C8A0B8381F996D00007BB700 /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | C8A0B8391F996D00007BB700 /* Build configuration list for PBXNativeTarget "JSONDecoderTests" */ = { 548 | isa = XCConfigurationList; 549 | buildConfigurations = ( 550 | C8A0B83A1F996D00007BB700 /* Debug */, 551 | C8A0B83B1F996D00007BB700 /* Release */, 552 | ); 553 | defaultConfigurationIsVisible = 0; 554 | defaultConfigurationName = Release; 555 | }; 556 | /* End XCConfigurationList section */ 557 | }; 558 | rootObject = C8A0B8191F996CFF007BB700 /* Project object */; 559 | } 560 | --------------------------------------------------------------------------------