├── 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 |
--------------------------------------------------------------------------------