TRAVIS_OS_NAME=osx <<<<<< ENV codecov.yml Example.playground/Contents.swift Example.playground/Resources/example.json Example.playground/contents.xcplayground Example.playground/playground.xcworkspace/contents.xcworkspacedata JSONUtilities.podspec JSONUtilities.xcodeproj/project.pbxproj JSONUtilities.xcodeproj/project.xcworkspace/contents.xcworkspacedata JSONUtilities.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings JSONUtilities.xcodeproj/xcshareddata/xcschemes/JSONUtilities.xcscheme LICENSE Metadata/Info.plist Metadata/JSONUtilities.h NonSwiftPackageManagerTests/BundleFileLoadingTests.swift Package.swift Sources/JSONUtilities/DecodingError.swift Sources/JSONUtilities/Dictionary+JSONKey.swift Sources/JSONUtilities/Dictionary+KeyPath.swift Sources/JSONUtilities/InvalidItemBehaviour.swift Sources/JSONUtilities/JSONFileLoading.swift Sources/JSONUtilities/JSONObjectConvertible.swift Sources/JSONUtilities/JSONPrimitiveConvertible.swift Sources/JSONUtilities/URL+JSONPrimitiveConvertible.swift Tests/JSONUtilitiesTests/Dictionary+KeyPathTests.swift Tests/JSONUtilitiesTests/FileLoadingTests.swift Tests/JSONUtilitiesTests/Helpers/Dictionary+Equatable.swift Tests/JSONUtilitiesTests/Helpers/XCTestCase+Additions.swift Tests/JSONUtilitiesTests/InlineDecodingTests.swift Tests/JSONUtilitiesTests/InvalidItemBehaviourTests.swift Tests/JSONUtilitiesTests/JSONDecodingTests.swift Tests/JSONUtilitiesTests/JSONPrimitiveConvertibleTests.swift Tests/JSONUtilitiesTests/Metadata/Info.plist Tests/JSONUtilitiesTests/Mocks/MockChild.swift Tests/JSONUtilitiesTests/Mocks/MockParent.swift Tests/JSONUtilitiesTests/Mocks/MockSimpleChild.swift Tests/JSONUtilitiesTests/Mocks/MockSimpleParent.swift Tests/JSONUtilitiesTests/TestFiles/JSONFiles.swift Tests/JSONUtilitiesTests/TestFiles/correct.json Tests/JSONUtilitiesTests/TestFiles/correct_with_missing_nested_array.json Tests/JSONUtilitiesTests/TestFiles/correct_with_missing_raw_array.json Tests/JSONUtilitiesTests/TestFiles/correct_without_nested_object.json Tests/JSONUtilitiesTests/TestFiles/empty.json Tests/JSONUtilitiesTests/TestFiles/invalid.json Tests/JSONUtilitiesTests/TestFiles/root_array.json <<<<<< network # path=JSONUtilitiesTests.xctest.coverage.txt /Users/travis/build/lucianomarisi/JSONUtilities/NonSwiftPackageManagerTests/BundleFileLoadingTests.swift: 1| |// 2| |// BundleFileLoadingTests.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 17/07/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |class BundleFileLoadingTests: XCTestCase { 13| | 14| 1| func testLoadFromBundle() { 15| 1| do { 16| 1| let _ = try JSONDictionary.from(filename: JSONFilename.correct, bundle: testBundle) 17| 1| } catch { 18| 0| XCTFail("Unexpected error: \(error)") 19| 1| } 20| 1| } 21| | 22| 1| func testAttemptToLoadMissingFileFromBundle() { 23| 1| do { 24| 1| let _ = try JSONDictionary.from(filename: JSONFilename.missing, bundle: testBundle) 25| 1| } catch let error as JSONUtilsError { 26| 1| XCTAssertEqual(error, JSONUtilsError.couldNotFindFile) 27| 1| } catch { 28| 0| XCTFail("Unexpected error: \(error)") 29| 1| } 30| 1| } 31| | 32| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Dictionary+KeyPathTests.swift: 1| |// 2| |// Dictionary+KeyPathTests.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 04/09/2016. 6| |// Copyright © 2016 Luciano Marisi. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |class Dictionary_KeyPathTests: XCTestCase { 13| | 14| 1| func testAccessOneLevelDeep() { 15| 1| let expectedValue = "value" 16| 1| let dictionary = [ 17| 1| "root_key": [ 18| 1| "first_level_key": expectedValue 19| 1| ] 20| 1| ] 21| 1| guard let calculatedValue = dictionary[keyPath: "root_key.first_level_key"] as? String else { 22| 0| XCTFail(#function) 23| 0| return 24| 1| } 25| 1| XCTAssertEqual(calculatedValue, expectedValue) 26| 1| } 27| | 28| 1| func testAccessTwoLevelDeep() { 29| 1| let expectedValue = "value" 30| 1| let dictionary = [ 31| 1| "root_key": [ 32| 1| "first_level_key": [ 33| 1| "second_level_key": expectedValue 34| 1| ] 35| 1| ] 36| 1| ] 37| 1| guard let calculatedValue = dictionary[keyPath: "root_key.first_level_key.second_level_key"] as? String else { 38| 0| XCTFail(#function) 39| 0| return 40| 1| } 41| 1| XCTAssertEqual(calculatedValue, expectedValue) 42| 1| } 43| | 44| 1| func testAccessRepeatedKey_InSequence_OneLevelDeep() { 45| 1| let expectedValue = "value" 46| 1| let dictionary = [ 47| 1| "root_key": [ 48| 1| "root_key": expectedValue 49| 1| ] 50| 1| ] 51| 1| guard let calculatedValue = dictionary[keyPath: "root_key.root_key"] as? String else { 52| 0| XCTFail(#function) 53| 0| return 54| 1| } 55| 1| XCTAssertEqual(calculatedValue, expectedValue) 56| 1| } 57| | 58| 1| func testAccessRepeatedKey_InSequence_TwoLevelsDeep() { 59| 1| let expectedValue = "value" 60| 1| let dictionary = [ 61| 1| "root_key": [ 62| 1| "root_key": [ 63| 1| "root_key": expectedValue 64| 1| ] 65| 1| ] 66| 1| ] 67| 1| guard let calculatedValue = dictionary[keyPath: "root_key.root_key.root_key"] as? String else { 68| 0| XCTFail(#function) 69| 0| return 70| 1| } 71| 1| XCTAssertEqual(calculatedValue, expectedValue) 72| 1| } 73| | 74| 1| func testAccessRepeatedKey_InBetweenSequence_TwoLevelsDeep() { 75| 1| let expectedValue = "value" 76| 1| let dictionary = [ 77| 1| "root_key": [ 78| 1| "first_level_key": [ 79| 1| "root_key": expectedValue 80| 1| ] 81| 1| ] 82| 1| ] 83| 1| guard let calculatedValue = dictionary[keyPath: "root_key.first_level_key.root_key"] as? String else { 84| 0| XCTFail(#function) 85| 0| return 86| 1| } 87| 1| XCTAssertEqual(calculatedValue, expectedValue) 88| 1| } 89| | 90| 1| func testAccess_MalformedKeyPath_OneLevelDeep() { 91| 1| let expectedValue = "value" 92| 1| let dictionary = [ 93| 1| "root_key": [ 94| 1| "first_level_key": expectedValue 95| 1| ] 96| 1| ] 97| 1| 98| 1| let calculatedValue = dictionary[keyPath: "root_key..first_level_key"] as? String 99| 1| XCTAssertNil(calculatedValue) 100| 1| } 101| | 102| 1| func testAccess_KeyPathEndsWithADot_OneLevelDeep() { 103| 1| let expectedValue = "value" 104| 1| let dictionary = [ 105| 1| "root_key": [ 106| 1| "first_level_key": expectedValue 107| 1| ] 108| 1| ] 109| 1| 110| 1| guard let calculatedValue = dictionary[keyPath: "root_key.first_level_key."] as? String else { 111| 0| XCTFail(#function) 112| 0| return 113| 1| } 114| 1| XCTAssertEqual(calculatedValue, expectedValue) 115| 1| } 116| | 117| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/FileLoadingTests.swift: 1| |// 2| |// FileLoadingTests.swift 3| |// FileLoadingTests 4| |// 5| |// Created by Luciano Marisi on 21/11/2015. 6| |// Copyright © 2015 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |typealias NoEscapeFunction = ( () throws -> Void) 13| | 14| |class FileLoadingTests: XCTestCase { 15| | 16| 1| func testLoadingJSONFile() { 17| 1| do { 18| 1| try JSONDictionary.from(url: JSONFilePath.correct) 19| 1| } catch { 20| 0| XCTFail("Unexpected error: \(error)") 21| 1| } 22| 1| } 23| | 24| 1| func testLoadingJSONFileLoadingFailed() { 25| 1| expectError(.fileLoadingFailed) { 26| 1| try JSONDictionary.from(url: JSONFilePath.missing) 27| 1| } 28| 1| } 29| | 30| 1| func testLoadingJSONFileDeserializationFailed() { 31| 1| expectError(.fileDeserializationFailed) { 32| 1| try JSONDictionary.from(url: JSONFilePath.invalid) 33| 1| } 34| 1| } 35| | 36| 1| func testLoadingJSONFileNotAJSONDictionary() { 37| 1| expectError(.fileNotAJSONDictionary) { 38| 1| try JSONDictionary.from(url: JSONFilePath.rootArray) 39| 1| } 40| 1| } 41| | 42| | // MARK: Helpers 43| | 44| 3| fileprivate func expectError(_ expectedError: JSONUtilsError, file: StaticString = #file, line: UInt = #line, block: NoEscapeFunction ) { 45| 3| do { 46| 3| try block() 47| 3| } catch let error { 48| 3| XCTAssert(error is JSONUtilsError, file: file, line: line) 49| 3| if let jsonUtilsError = error as? JSONUtilsError { 50| 3| XCTAssertEqual(jsonUtilsError, expectedError, file: file, line: line) 51| 3| } 52| 3| return 53| 3| } 54| 0| XCTFail("No error thrown, expected: \(expectedError)", file: file, line: line) 55| 0| } 56| | 57| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Helpers/Dictionary+Equatable.swift: 1| |// 2| |// Dictionary+Equatable.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 08/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| 2|public func == (lhs: [String: Any], rhs: [String: Any] ) -> Bool { 12| 2| return NSDictionary(dictionary: lhs).isEqual(to: rhs) 13| 2|} 14| | 15| 2|public func == (lhs: [String: Any]?, rhs: [String: Any]? ) -> Bool { 16| 2| guard let lhs = lhs, let rhs = rhs else { return false } 17| 2| return NSDictionary(dictionary: lhs).isEqual(to: rhs) 18| 2|} 19| | 20| 2|public func == (lhs: [[String: Any]], rhs: [[String: Any]] ) -> Bool { 21| 2| let lhsArray = NSArray(array: lhs) 22| 2| let rhsArray = NSArray(array: rhs) 23| 2| return lhsArray.isEqual(to: rhsArray as [AnyObject]) 24| 2|} 25| | 26| 2|public func == (lhs: [[String: Any]]?, rhs: [[String: Any]]? ) -> Bool { 27| 2| guard let lhs = lhs, let rhs = rhs else { return false } 28| 2| let lhsArray = NSArray(array: lhs) 29| 2| let rhsArray = NSArray(array: rhs) 30| 2| return lhsArray.isEqual(to: rhsArray as [AnyObject]) 31| 2|} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Helpers/XCTestCase+Additions.swift: 1| |// 2| |// XCTestCase+Additions.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 15/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import XCTest 10| |import JSONUtilities 11| | 12| |extension XCTestCase { 13| | 14| 2| var testBundle: Bundle { 15| 2| return Bundle(for: type(of: self)) 16| 2| } 17| | 18| 20| func expectNoError(decode: () throws -> Void) { 19| 20| do { 20| 20| try decode() 21| 20| } catch { 22| 0| XCTFail("Should not throw error") 23| 20| } 24| 20| } 25| | 26| 31| func expectDecodingError(reason: JSONUtilities.DecodingError.Reason, keyPath: String, decode: () throws -> Void) { 27| 31| do { 28| 31| try decode() 29| 31| XCTFail("Decoding was supposed to throw \"\(reason)\" error") 30| 31| } catch { 31| 31| guard let error = error as? JSONUtilities.DecodingError else { 32| 0| XCTFail("Error is not a Decoding Error") 33| 0| return 34| 31| } 35| 31| XCTAssertTrue(error.reason == reason, "DecodingError failed because of \"\(error.reason)\" but was supposed to fail for \"\(reason)\"") 36| 31| XCTAssertTrue(error.keyPath == keyPath, "DecodingError failed at keyPath \"\(error.keyPath)\", but was supposed to fail at \"\(keyPath)\"") 37| 31| } 38| 31| } 39| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/InlineDecodingTests.swift: 1| |// 2| |// InlineDecodingTests.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 15/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |private let randomKey = "aaaaaaa" 13| | 14| |class InlineDecodingTests: XCTestCase { 15| | 16| 1| func testDecodingOfJSONRawTypes() { 17| 1| let expectedInt: Int = 1 18| 1| expectDecodeType(expectedInt) 19| 1| 20| 1| let expectedFloat: Float = 2.2 21| 1| expectDecodeType(expectedFloat) 22| 1| 23| 1| let expectedDouble: Double = 2.2 24| 1| expectDecodeType(expectedDouble) 25| 1| 26| 1| let expectedString: String = "something" 27| 1| expectDecodeType(expectedString) 28| 1| 29| 1| let expectedBool: Bool = true 30| 1| expectDecodeType(expectedBool) 31| 1| } 32| | 33| 1| func testDecodingOfJSONDictionary() { 34| 1| 35| 1| let expectedValue: JSONDictionary = ["key1": "value1", "key2": "value2"] 36| 1| let dictionary = ["key": expectedValue] 37| 1| let decodedValue: JSONDictionary = try! dictionary.json(atKeyPath: "key") 38| 1| XCTAssert(decodedValue == expectedValue) 39| 1| 40| 1| let decodedOptionalInt: JSONDictionary? = dictionary.json(atKeyPath: "key") 41| 1| XCTAssert(decodedOptionalInt == expectedValue) 42| 1| 43| 1| expectDecodingError(reason: .keyNotFound, keyPath: randomKey) { 44| 1| let _ : JSONDictionary = try dictionary.json(atKeyPath: randomKey) 45| 1| } 46| 1| 47| 1| let decodedMissingInt: JSONDictionary? = dictionary.json(atKeyPath: randomKey) 48| 1| XCTAssertNil(decodedMissingInt) 49| 1| } 50| | 51| 1| func testDecodingOfJSONRawTypesArray() { 52| 1| let expectedInt: [Int] = [1] 53| 1| expectDecodeTypeArray(expectedInt) 54| 1| 55| 1| let expectedFloat: [Float] = [2.2] 56| 1| expectDecodeTypeArray(expectedFloat) 57| 1| 58| 1| let expectedDouble: [Double] = [2.2] 59| 1| expectDecodeTypeArray(expectedDouble) 60| 1| 61| 1| let expectedString: [String] = ["something"] 62| 1| expectDecodeTypeArray(expectedString) 63| 1| 64| 1| let expectedBool: [Bool] = [true] 65| 1| expectDecodeTypeArray(expectedBool) 66| 1| } 67| | 68| 1| func testIncorrectEnum() { 69| 1| 70| 1| let dictionary = ["enum": "three"] 71| 1| 72| 1| expectDecodingError(reason: .keyNotFound, keyPath: "enumIncorrect") { 73| 1| let _ : MockParent.MockEnum = try dictionary.json(atKeyPath: "enumIncorrect") 74| 1| } 75| 1| 76| 1| expectDecodingError(reason: .incorrectRawRepresentableRawValue, keyPath: "enum") { 77| 1| let _ : MockParent.MockEnum = try dictionary.json(atKeyPath: "enum") 78| 1| } 79| 1| } 80| | 81| 1| func test_decodingMandatoryEnumDictionary_withKey() { 82| 1| let dictionary: JSONDictionary = ["enums": ["value1": "one", "value2": "!@1", "value3": "two"]] 83| 1| 84| 1| let decodedEnums: [String: MockParent.MockEnum] = try! dictionary.json(atKeyPath: "enums") 85| 1| 86| 1| let expectedEnums: [String: MockParent.MockEnum] = ["value1": .one, "value3": .two] 87| 1| XCTAssertEqual(decodedEnums, expectedEnums) 88| 1| } 89| | 90| 1| func test_decodingOptionalEnumDictionary_withKey() { 91| 1| let dictionary: JSONDictionary = ["enums": ["value1": "one", "value2": "!@1", "value3": "two"]] 92| 1| 93| 1| let decodedEnums: [String: MockParent.MockEnum]? = dictionary.json(atKeyPath: "enums") 94| 1| 95| 1| let expectedEnums: [String: MockParent.MockEnum] = ["value1": .one, "value3": .two] 96| 1| XCTAssertEqual(decodedEnums!, expectedEnums) 97| 1| } 98| | 99| 1| func test_decodingMandatoryEnumArray_withKey() { 100| 1| let dictionary: JSONDictionary = ["enums": ["one", "!@1", "two"]] 101| 1| 102| 1| let decodedEnums: [MockParent.MockEnum] = try! dictionary.json(atKeyPath: "enums") 103| 1| 104| 1| let expectedEnums: [MockParent.MockEnum] = [.one, .two] 105| 1| XCTAssertEqual(decodedEnums, expectedEnums) 106| 1| } 107| | 108| 1| func test_decodingOptionalEnumArray_withKey() { 109| 1| let dictionary: JSONDictionary = ["enums": ["one", "!@1", "two"]] 110| 1| 111| 1| let decodedEnums: [MockParent.MockEnum]? = dictionary.json(atKeyPath: "enums") 112| 1| 113| 1| let expectedEnums: [MockParent.MockEnum] = [.one, .two] 114| 1| XCTAssertEqual(decodedEnums!, expectedEnums) 115| 1| } 116| | 117| 1| func test_decodingMandatoryEnumArray_withoutKey() { 118| 1| let dictionary: JSONDictionary = ["enums": ["one", "!@1", "two"]] 119| 1| 120| 1| expectDecodingError(reason: .keyNotFound, keyPath: "invalid_key") { 121| 1| let _ : MockParent.MockEnum = try dictionary.json(atKeyPath: "invalid_key") 122| 1| } 123| 1| } 124| | 125| 1| func test_decodingOptionalEnumArray_withoutKey() { 126| 1| let dictionary: JSONDictionary = ["enums": ["one", "!@1", "two"]] 127| 1| 128| 1| let decodedEnums: [MockParent.MockEnum]? = dictionary.json(atKeyPath: "invalid_key") 129| 1| 130| 1| XCTAssertNil(decodedEnums) 131| 1| } 132| | 133| 1| func testDecodingOfJSONDictionaryArray() { 134| 1| 135| 1| let expectedValue: [JSONDictionary] = [["key1": "value1"], ["key2": "value2"]] 136| 1| let dictionary = ["key": expectedValue] 137| 1| let decodedValue: [JSONDictionary] = try! dictionary.json(atKeyPath: "key") 138| 1| XCTAssert(decodedValue == expectedValue) 139| 1| 140| 1| let decodedOptionalInt: [JSONDictionary]? = dictionary.json(atKeyPath: "key") 141| 1| XCTAssert(decodedOptionalInt == expectedValue) 142| 1| 143| 1| expectDecodingError(reason: .keyNotFound, keyPath: randomKey) { 144| 1| let _ : [JSONDictionary] = try dictionary.json(atKeyPath: randomKey) 145| 1| } 146| 1| 147| 1| let decodedMissingInt: [JSONDictionary]? = dictionary.json(atKeyPath: randomKey) 148| 1| XCTAssertNil(decodedMissingInt) 149| 1| } 150| | 151| 1| func testSomeInvalidDecodableTypes() { 152| 1| let parentDictionary: JSONDictionary = ["children": ["john", ["name": "jane"]]] 153| 1| let decodedParent: MockSimpleParent = try! MockSimpleParent(jsonDictionary: parentDictionary) 154| 1| XCTAssert(decodedParent.children.count == 1) 155| 1| } 156| | 157| | // MARK: DecodingErrors 158| | 159| 1| func testDecodingErrorDescriptions() { 160| 1| let failureReasons: [JSONUtilities.DecodingError.Reason] = [ 161| 1| .keyNotFound, 162| 1| .incorrectRawRepresentableRawValue, 163| 1| .incorrectType, 164| 1| .conversionFailure 165| 1| ] 166| 4| failureReasons.forEach { 167| 4| let error = DecodingError(dictionary: [:], keyPath: "", expectedType: String.self, value: "", array: nil, reason: $0) 168| 4| XCTAssert(error.description.count > 0) 169| 4| XCTAssert(error.debugDescription.count > 0) 170| 4| XCTAssert(error.reason.description.count > 0) 171| 4| } 172| 1| } 173| | 174| | // MARK: Helpers 175| | 176| 5| fileprivate func expectDecodeType(_ expectedValue: ExpectedType, file: StaticString = #file, line: UInt = #line) { 177| 5| 178| 5| let dictionary = ["key": expectedValue] 179| 5| let decodedValue: ExpectedType = try! dictionary.json(atKeyPath: "key") 180| 5| XCTAssertEqual(decodedValue, expectedValue, file: file, line: line) 181| 5| 182| 5| let decodedOptionalInt: ExpectedType? = dictionary.json(atKeyPath: "key") 183| 5| XCTAssertEqual(decodedOptionalInt!, expectedValue, file: file, line: line) 184| 5| 185| 5| expectDecodingError(reason: .keyNotFound, keyPath: randomKey) { 186| 5| let _ : ExpectedType = try dictionary.json(atKeyPath: randomKey) 187| 5| } 188| 5| 189| 5| let decodedMissingInt: ExpectedType? = dictionary.json(atKeyPath: randomKey) 190| 5| XCTAssertNil(decodedMissingInt) 191| 5| } 192| | 193| 5| fileprivate func expectDecodeTypeArray(_ expectedValue: [ExpectedType], file: StaticString = #file, line: UInt = #line) { 194| 5| 195| 5| let dictionary = ["key": expectedValue] 196| 5| let decodedValue: [ExpectedType] = try! dictionary.json(atKeyPath: "key") 197| 5| XCTAssertEqual(decodedValue, expectedValue, file: file, line: line) 198| 5| 199| 5| let decodedOptionalInt: [ExpectedType]? = dictionary.json(atKeyPath: "key") 200| 5| XCTAssertEqual(decodedOptionalInt!, expectedValue, file: file, line: line) 201| 5| 202| 5| expectDecodingError(reason: .keyNotFound, keyPath: randomKey) { 203| 5| let _ : [ExpectedType] = try dictionary.json(atKeyPath: randomKey) 204| 5| } 205| 5| 206| 5| let decodedMissingInt: [ExpectedType]? = dictionary.json(atKeyPath: randomKey) 207| 5| XCTAssertNil(decodedMissingInt) 208| 5| } 209| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/InvalidItemBehaviourTests.swift: 1| |// 2| |// InvalidItemBehaviourTests.swift 3| |// JSONUtilities 4| |// 5| |// Created by Yonas Kolb on 21/4/17. 6| |// Copyright © 2017 Luciano Marisi. All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |private let randomKey = "aaaaaaa" 13| | 14| |class InvalidItemBehaviourTests: XCTestCase { 15| | 16| | let key = "key" 17| | 18| | let dictionaryString = [ 19| | "key": [ 20| | "key1": "value1", 21| | "key2": 2 22| | ] 23| | ] 24| | 25| | let dictionaryConvertible = [ 26| | "key": [ 27| | "key1": "www.google.com", 28| | "key2": 2 29| | ] 30| | ] 31| | 32| | let dictionaryMockChild = [ 33| | "key": [ 34| | "key1": ["name": "john"], 35| | "key2": 2 36| | ] 37| | ] 38| | 39| | let arrayString = [ 40| | "key": [ 41| | "value1", 42| | 2 43| | ] 44| | ] 45| | 46| | let arrayConvertible = [ 47| | "key": [ 48| | "www.google.com", 49| | 2 50| | ] 51| | ] 52| | 53| | let arrayMockChild = [ 54| | "key": [ 55| | ["name": "john"], 56| | 2 57| | ] 58| | ] 59| | 60| | // MARK: Dictionary InvalidItemBehaviour.fail 61| | 62| 1| func test_stringJSONRawTypeDictionaryFails_whenThereAreInvalidObjects_and_invalidItemBehaviourIsFail() { 63| 1| expectDecodingError(reason: .incorrectType, keyPath: "key2") { 64| 1| let _ : [String: String] = try dictionaryString.json(atKeyPath: key, invalidItemBehaviour: .fail) 65| 1| } 66| 1| } 67| | 68| 1| func test_stringJSONPrimitiveConvertibleDictionaryFails_whenThereAreInvalidObjects_and_invalidItemBehaviourIsFail() { 69| 1| expectDecodingError(reason: .incorrectType, keyPath: "key2") { 70| 1| let _ : [String: URL] = try dictionaryConvertible.json(atKeyPath: key, invalidItemBehaviour: .fail) 71| 1| } 72| 1| } 73| | 74| 1| func test_stringJSONObjectConvertibleDictionaryFails_whenThereAreInvalidObjects_and_invalidItemBehaviourIsFail() { 75| 1| expectDecodingError(reason: .incorrectType, keyPath: "key2") { 76| 1| let _ : [String: MockSimpleChild] = try dictionaryMockChild.json(atKeyPath: key, invalidItemBehaviour: .fail) 77| 1| } 78| 1| } 79| | 80| | // MARK: Dictionary InvalidItemBehaviour.remove 81| | 82| 1| func test_stringJSONRawTypeDictionary_removesInvalidObjects_invalidItemBehaviourIsRemove() { 83| 1| expectNoError { 84| 1| let decodedDictionary: [String: String] = try dictionaryString.json(atKeyPath: key, invalidItemBehaviour: .remove) 85| 1| XCTAssert(decodedDictionary.count == 1) 86| 1| } 87| 1| } 88| | 89| 1| func test_stringJSONPrimitiveConvertibleDictionary_removesInvalidObjects_invalidItemBehaviourIsRemove() { 90| 1| expectNoError { 91| 1| let decodedDictionary: [String: URL] = try dictionaryConvertible.json(atKeyPath: key, invalidItemBehaviour: .remove) 92| 1| XCTAssert(decodedDictionary.count == 1) 93| 1| } 94| 1| } 95| | 96| 1| func test_stringJSONObjectConvertibleDictionary_removesInvalidObjects_invalidItemBehaviourIsRemove() { 97| 1| expectNoError { 98| 1| let decodedDictionary: [String: MockSimpleChild] = try dictionaryMockChild.json(atKeyPath: key, invalidItemBehaviour: .remove) 99| 1| XCTAssert(decodedDictionary.count == 1) 100| 1| } 101| 1| } 102| | 103| | // MARK: Array InvalidItemBehaviour.fail 104| | 105| 1| func test_stringJSONRawTypeArrayFails_whenThereAreInvalidObjects_and_invalidItemBehaviourIsFail() { 106| 1| expectDecodingError(reason: .incorrectType, keyPath: key) { 107| 1| let _ : [String: String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .fail) 108| 1| } 109| 1| } 110| | 111| 1| func test_stringJSONPrimitiveConvertibleArrayFails_whenThereAreInvalidObjects_and_invalidItemBehaviourIsFail() { 112| 1| expectDecodingError(reason: .incorrectType, keyPath: key) { 113| 1| let _ : [URL] = try arrayConvertible.json(atKeyPath: key, invalidItemBehaviour: .fail) 114| 1| } 115| 1| } 116| | 117| 1| func test_stringJSONObjectConvertibleArrayFails_whenThereAreInvalidObjects_and_invalidItemBehaviourIsFail() { 118| 1| expectDecodingError(reason: .incorrectType, keyPath: key) { 119| 1| let _ : [MockSimpleChild] = try arrayMockChild.json(atKeyPath: key, invalidItemBehaviour: .fail) 120| 1| } 121| 1| } 122| | 123| | // MARK: Array InvalidItemBehaviour.remove 124| | 125| 1| func test_stringJSONRawTypeArray_removesInvalidObjects_invalidItemBehaviourIsRemove() { 126| 1| expectNoError { 127| 1| let decodedDictionary: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .remove) 128| 1| XCTAssert(decodedDictionary.count == 1) 129| 1| } 130| 1| } 131| | 132| 1| func test_stringJSONPrimitiveConvertibleArray_removesInvalidObjects_invalidItemBehaviourIsRemove() { 133| 1| expectNoError { 134| 1| let decodedDictionary: [URL] = try arrayConvertible.json(atKeyPath: key, invalidItemBehaviour: .remove) 135| 1| XCTAssert(decodedDictionary.count == 1) 136| 1| } 137| 1| } 138| | 139| 1| func test_stringJSONObjectConvertibleArray_removesInvalidObjects_invalidItemBehaviourIsRemove() { 140| 1| expectNoError { 141| 1| let decodedDictionary: [MockSimpleChild] = try arrayMockChild.json(atKeyPath: key, invalidItemBehaviour: .remove) 142| 1| XCTAssert(decodedDictionary.count == 1) 143| 1| } 144| 1| } 145| | 146| | // MARK: Dictionary InvalidItemBehaviour.value 147| | 148| 1| func test_stringJSONRawTypeDictionary_setsValue_invalidItemBehaviourIsValue() { 149| 1| expectNoError { 150| 1| let decodedDictionary: [String: String] = try dictionaryString.json(atKeyPath: key, invalidItemBehaviour: .value("default")) 151| 1| XCTAssert(decodedDictionary["key2"] == "default") 152| 1| } 153| 1| } 154| | 155| 1| func test_stringJSONPrimitiveConvertibleDictionary_setsValue_invalidItemBehaviourIsValue() { 156| 1| expectNoError { 157| 1| let decodedDictionary: [String: URL] = try dictionaryConvertible.json(atKeyPath: key, invalidItemBehaviour: .value(URL(string: "test.com")!)) 158| 1| XCTAssert(decodedDictionary["key2"]?.absoluteString == "test.com") 159| 1| } 160| 1| } 161| | 162| 1| func test_stringJSONObjectConvertibleDictionaryy_setsValue_invalidItemBehaviourIsValue() { 163| 1| expectNoError { 164| 1| let decodedDictionary: [String: MockSimpleChild] = try dictionaryMockChild.json(atKeyPath: key, invalidItemBehaviour: .value(MockSimpleChild(name: "default"))) 165| 1| XCTAssert(decodedDictionary["key2"]?.name == "default") 166| 1| } 167| 1| } 168| | 169| | // MARK: Dictionary InvalidItemBehaviour.custom 170| | 171| 1| func test_stringJSONRawTypeDictionary_setsValue_invalidItemBehaviourIsCustom() { 172| 1| expectNoError { 173| 1| let decodedDictionary: [String: String] = try dictionaryString.json(atKeyPath: key, invalidItemBehaviour: .custom({.value("\($0.value)")})) 174| 1| XCTAssert(decodedDictionary["key2"] == "2") 175| 1| } 176| 1| } 177| | 178| 1| func test_stringJSONPrimitiveConvertibleDictionary_setsValue_invalidItemBehaviourIsCustom() { 179| 1| expectNoError { 180| 1| let decodedDictionary: [String: URL] = try dictionaryConvertible.json(atKeyPath: key, invalidItemBehaviour: .custom({.value(URL(string: "\($0.value)")!)})) 181| 1| XCTAssert(decodedDictionary["key2"]?.absoluteString == "2") 182| 1| } 183| 1| } 184| | 185| 1| func test_stringJSONObjectConvertibleDictionary_setsValue_invalidItemBehaviourIsCalculateValue() { 186| 1| expectNoError { 187| 1| let decodedDictionary: [String: MockSimpleChild] = try dictionaryMockChild.json(atKeyPath: key, invalidItemBehaviour: .custom({.value(MockSimpleChild(name: "\($0.value)"))})) 188| 1| XCTAssert(decodedDictionary["key2"]?.name == "2") 189| 1| } 190| 1| } 191| | 192| | // MARK: Array InvalidItemBehaviour.value 193| | 194| 1| func test_stringJSONRawTypeArray_setsValue_invalidItemBehaviourIsValue() { 195| 1| expectNoError { 196| 1| let decodedDictionary: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .value("default")) 197| 1| XCTAssert(decodedDictionary.last == "default") 198| 1| } 199| 1| } 200| | 201| 1| func test_stringJSONPrimitiveConvertibleArray_setsValue_invalidItemBehaviourIsValue() { 202| 1| expectNoError { 203| 1| let decodedDictionary: [URL] = try arrayConvertible.json(atKeyPath: key, invalidItemBehaviour: .value(URL(string: "test.com")!)) 204| 1| XCTAssert(decodedDictionary.last?.absoluteString == "test.com") 205| 1| } 206| 1| } 207| | 208| 1| func test_stringJSONObjectConvertibleArray_setsValue_invalidItemBehaviourIsValue() { 209| 1| expectNoError { 210| 1| let decodedDictionary: [MockSimpleChild] = try arrayMockChild.json(atKeyPath: key, invalidItemBehaviour: .value(MockSimpleChild(name: "default"))) 211| 1| XCTAssert(decodedDictionary.last?.name == "default") 212| 1| } 213| 1| } 214| | 215| | // MARK: Array InvalidItemBehaviour.custom 216| | 217| 1| func test_stringJSONRawTypeArray_setsValue_invalidItemBehaviourIsCustom() { 218| 1| expectNoError { 219| 1| let decodedDictionary: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .custom({ error in 220| 1| .value("\(error.value)") 221| 1| })) 222| 1| XCTAssert(decodedDictionary.last == "2") 223| 1| } 224| 1| } 225| | 226| 1| func test_stringJSONPrimitiveConvertibleArray_setsValue_invalidItemBehaviourIsCustom() { 227| 1| expectNoError { 228| 1| let decodedDictionary: [URL] = try arrayConvertible.json(atKeyPath: key, invalidItemBehaviour: .custom({ error in 229| 1| .value(URL(string: "\(error.value)")!) 230| 1| })) 231| 1| XCTAssert(decodedDictionary.last?.absoluteString == "2") 232| 1| } 233| 1| } 234| | 235| 1| func test_stringJSONObjectConvertibleArray_setsValue_invalidItemBehaviourIsCustom() { 236| 1| expectNoError { 237| 1| let decodedDictionary: [MockSimpleChild] = try arrayMockChild.json(atKeyPath: key, invalidItemBehaviour: .custom({ error in 238| 1| .value(MockSimpleChild(name: "\(error.value)")) 239| 1| })) 240| 1| XCTAssert(decodedDictionary.last?.name == "2") 241| 1| } 242| 1| } 243| | 244| | // MARK: InvalidItemBehaviour.custom 245| | 246| 1| func test_invalidItemBehaviourIsCustom_returnsValue() { 247| 1| expectNoError { 248| 1| let array: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .custom({ _ in .value("2") })) 249| 1| XCTAssert(array.count == 2) 250| 1| } 251| 1| } 252| | 253| 1| func test_invalidItemBehaviourIsCustom_returnsRemove() { 254| 1| expectNoError { 255| 1| let array: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .custom({ _ in .remove })) 256| 1| XCTAssert(array.count == 1) 257| 1| } 258| 1| } 259| | 260| 1| func test_invalidItemBehaviourIsCustom_returnsFail() { 261| 1| expectDecodingError(reason: .incorrectType, keyPath: key) { 262| 1| let _: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .custom({ _ in .fail })) 263| 1| } 264| 1| } 265| | 266| 1| func test_invalidItemBehaviourIsCustom_returnsCustom() { 267| 1| expectDecodingError(reason: .incorrectType, keyPath: key) { 268| 1| let _: [String] = try arrayString.json(atKeyPath: key, invalidItemBehaviour: .custom({ _ in .custom({_ in .fail}) })) 269| 1| } 270| 1| } 271| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/JSONDecodingTests.swift: 1| |// 2| |// JSONDecodingTests.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 21/11/2015. 6| |// Copyright © 2015 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |class JSONDecodingTests: XCTestCase { 13| | 14| | let expectedChild = MockChild(string: "stringValue", integer: 1, double: 1.2, bool: true) 15| | let expectedDictionary: JSONDictionary = ["doubleKey": 1.2, "integerKey": 1, "stringKey": "stringValue", "boolKey": true] 16| | let expectedDictionaryArray: [JSONDictionary] = [ 17| | ["doubleKey": 1.2, "integerKey": 1, "stringKey": "stringValue", "boolKey": true], 18| | ["doubleKey": 1.2, "integerKey": 1, "stringKey": "stringValue", "boolKey": true], 19| | ["randomTypeObject": 123] 20| | ] 21| | 22| 1| func testCorrectDecodingForMandatoryJSONOnParentWithChild() { 23| 1| do { 24| 1| let jsonDictionary = try JSONDictionary.from(url: JSONFilePath.correct) 25| 1| let mockJSONParent = try MockParent(jsonDictionary: jsonDictionary) 26| 1| XCTAssertEqual(mockJSONParent.mandatoryString, "stringValue") 27| 1| XCTAssertEqual(mockJSONParent.mandatoryInt, 1) 28| 1| XCTAssertEqual(mockJSONParent.mandatoryDouble, 1.2) 29| 1| XCTAssertEqual(mockJSONParent.mandatoryBool, true) 30| 1| XCTAssertTrue(mockJSONParent.mandatoryWeakDictionaryKey == expectedDictionary) 31| 1| XCTAssertEqual(mockJSONParent.mandatoryCustomJSONObject, expectedChild) 32| 1| XCTAssertEqual(mockJSONParent.mandatoryEnum, MockParent.MockEnum.one) 33| 1| 34| 1| XCTAssertEqual(mockJSONParent.optionalExistingString, "stringValue") 35| 1| XCTAssertEqual(mockJSONParent.optionalExistingInt, 1) 36| 1| XCTAssertEqual(mockJSONParent.optionalExistingDouble, 1.2) 37| 1| XCTAssertEqual(mockJSONParent.optionalExistingBool, true) 38| 1| XCTAssertTrue(mockJSONParent.optionalExistingWeakDictionaryKey == expectedDictionary) 39| 1| XCTAssertEqual(mockJSONParent.optionalExistingCustomJSONObject, expectedChild) 40| 1| XCTAssertEqual(mockJSONParent.optionalExistingEnum, MockParent.MockEnum.one) 41| 1| 42| 1| XCTAssertNil(mockJSONParent.optionalMissingString) 43| 1| XCTAssertNil(mockJSONParent.optionalMissingInt) 44| 1| XCTAssertNil(mockJSONParent.optionalMissingDouble) 45| 1| XCTAssertNil(mockJSONParent.optionalMissingBool) 46| 1| XCTAssertNil(mockJSONParent.optionalMissingWeakDictionaryKey) 47| 1| XCTAssertNil(mockJSONParent.optionalMissingCustomJSONObject) 48| 1| XCTAssertNil(mockJSONParent.optionalMissingEnum) 49| 1| 50| 1| XCTAssertEqual(mockJSONParent.mandatoryArrayString, ["1", "2"]) 51| 1| XCTAssertEqual(mockJSONParent.mandatoryArrayInt, [1, 2]) 52| 1| XCTAssertEqual(mockJSONParent.mandatoryArrayDouble, [1.1, 1.2]) 53| 1| XCTAssertEqual(mockJSONParent.mandatoryArrayBool, [true, false]) 54| 1| XCTAssertTrue(mockJSONParent.mandatoryWeakDictionaryArrayKey == expectedDictionaryArray) 55| 1| XCTAssertEqual(mockJSONParent.mandatoryArrayCustomJSONObject, [expectedChild, expectedChild]) 56| 1| 57| 1| XCTAssertEqual(mockJSONParent.optionalExistingArrayString!, ["1", "2"]) 58| 1| XCTAssertEqual(mockJSONParent.optionalExistingArrayInt!, [1, 2]) 59| 1| XCTAssertEqual(mockJSONParent.optionalExistingArrayDouble!, [1.1, 1.2]) 60| 1| XCTAssertEqual(mockJSONParent.optionalExistingArrayBool!, [true, false]) 61| 1| XCTAssertTrue(mockJSONParent.optionalExistingWeakDictionaryArrayKey == expectedDictionaryArray) 62| 1| XCTAssertEqual(mockJSONParent.optionalExistingArrayCustomJSONObject!, [expectedChild, expectedChild]) 63| 1| 64| 1| XCTAssertNil(mockJSONParent.optionalMissingArrayString) 65| 1| XCTAssertNil(mockJSONParent.optionalMissingArrayInt) 66| 1| XCTAssertNil(mockJSONParent.optionalMissingArrayDouble) 67| 1| XCTAssertNil(mockJSONParent.optionalMissingArrayBool) 68| 1| XCTAssertNil(mockJSONParent.optionalMissingWeakDictionaryArrayKey) 69| 1| XCTAssertNil(mockJSONParent.optionalMissingArrayCustomJSONObject) 70| 1| 71| 1| XCTAssertEqual(mockJSONParent.mandatoryIntDictionary, ["value1": 1, "value2": 2]) 72| 1| XCTAssertEqual(mockJSONParent.mandatoryObjectDictionary, ["value1": expectedChild, "value2": expectedChild]) 73| 1| XCTAssertEqual(mockJSONParent.mandatoryURLDictionary, ["value1": URL(string: "https://google.com")!, "value2": URL(string: "https://apple.com")!]) 74| 1| XCTAssertEqual(mockJSONParent.optionalIntDictionary!, ["value1": 1, "value2": 2]) 75| 1| XCTAssertEqual(mockJSONParent.optionalObjectDictionary!, ["value1": expectedChild, "value2": expectedChild]) 76| 1| XCTAssertEqual(mockJSONParent.optionalURLDictionary!, ["value1": URL(string: "https://google.com")!, "value2": URL(string: "https://apple.com")!]) 77| 1| 78| 1| } catch { 79| 0| XCTFail("Unexpected error: \(error)") 80| 1| } 81| 1| } 82| | 83| 1| func testIncorrectDecodingForMandatoryJSONRawType() { 84| 1| expectDecodingError(reason: .keyNotFound, keyPath: "keypath.mandatoryStringKey") { 85| 1| let jsonDictionary = try JSONDictionary.from(url: JSONFilePath.empty) 86| 1| let _ = try MockParent(jsonDictionary: jsonDictionary) 87| 1| } 88| 1| } 89| | 90| 1| func testIncorrectDecodingForMandatoryJSONRawTypeArray() { 91| 1| expectDecodingError(reason: .keyNotFound, keyPath: "keypath.mandatoryArrayStringKey") { 92| 1| let jsonDictionary = try JSONDictionary.from(url: JSONFilePath.correctWithoutRawArray) 93| 1| let _ = try MockParent(jsonDictionary: jsonDictionary) 94| 1| } 95| 1| } 96| | 97| 1| func testIncorrectDecodingForMandatoryJSONNestedObject() { 98| 1| expectDecodingError(reason: .keyNotFound, keyPath: "keypath.mandatoryCustomJSONObjectKey") { 99| 1| let jsonDictionary = try JSONDictionary.from(url: JSONFilePath.correctWithoutNested) 100| 1| let _ = try MockParent(jsonDictionary: jsonDictionary) 101| 1| } 102| 1| } 103| | 104| 1| func testIncorrectDecodingForMandatoryJSONNestedObjectArray() { 105| 1| expectDecodingError(reason: .keyNotFound, keyPath: "keypath.mandatoryArrayCustomJSONObjectKey") { 106| 1| let jsonDictionary = try JSONDictionary.from(url: JSONFilePath.correctWithoutNestedArray) 107| 1| let _ = try MockParent(jsonDictionary: jsonDictionary) 108| 1| } 109| 1| } 110| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/JSONPrimitiveConvertibleTests.swift: 1| |// 2| |// JSONPrimitiveConvertibleTests.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 13/03/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import XCTest 10| |@testable import JSONUtilities 11| | 12| |private let invalidKey = "invalidKey" 13| | 14| |class JSONPrimitiveConvertibleTests: XCTestCase { 15| | 16| 1| func testDecodedAndTransformNSURL() { 17| 1| let urlString = "www.google.com" 18| 1| let expectedURL = URL(string: urlString) 19| 1| let jsonDictionary = [ 20| 1| "url": urlString, 21| 1| "invalid_url": "±" 22| 1| ] 23| 1| guard let mandatoryTransformedURL: URL = jsonDictionary.json(atKeyPath: "url") else { 24| 0| XCTFail(#function) 25| 0| return 26| 1| } 27| 1| XCTAssertEqual(expectedURL, mandatoryTransformedURL) 28| 1| 29| 1| let optionalTransformedURL: URL? = jsonDictionary.json(atKeyPath: "url") 30| 1| XCTAssertEqual(expectedURL, optionalTransformedURL) 31| 1| 32| 1| expectDecodingError(reason: .conversionFailure, keyPath: "invalid_url") { 33| 1| let _ : URL = try jsonDictionary.json(atKeyPath: "invalid_url") 34| 1| } 35| 1| } 36| | 37| 1| func testDecodedAndTransformNSURL_missingKey() { 38| 1| let jsonDictionary = ["url": "url"] 39| 1| 40| 1| expectDecodingError(reason: .keyNotFound, keyPath: invalidKey) { 41| 1| let _ : URL = try jsonDictionary.json(atKeyPath: invalidKey) 42| 1| } 43| 1| 44| 1| let urlFromMissingKey: URL? = jsonDictionary.json(atKeyPath: invalidKey) 45| 1| XCTAssertNil(urlFromMissingKey) 46| 1| } 47| | 48| 1| func testJSONPrimitiveConvertibleArray() { 49| 1| let expectedURLStrings = ["www.google.com", "www.apple.com"] 50| 2| let expectedURLs = expectedURLStrings.compactMap { URL(string: $0) } 51| 1| let jsonDictionary = ["urls": expectedURLStrings] 52| 1| let decodedURLs: [URL] = try! jsonDictionary.json(atKeyPath: "urls") 53| 1| XCTAssertEqual(decodedURLs, expectedURLs) 54| 1| 55| 1| expectDecodingError(reason: .keyNotFound, keyPath: invalidKey) { 56| 1| let _ : URL = try jsonDictionary.json(atKeyPath: invalidKey) 57| 1| } 58| 1| 59| 1| let decodedOptionalURLs: [URL]? = jsonDictionary.json(atKeyPath: "urls") 60| 1| XCTAssertEqual(decodedOptionalURLs!, expectedURLs) 61| 1| 62| 1| let decodedMissingURLs: [URL]? = jsonDictionary.json(atKeyPath: invalidKey) 63| 1| XCTAssertNil(decodedMissingURLs) 64| 1| } 65| | 66| 1| func testJSONPrimitiveConvertibleArray_failsOnNonTransformable() { 67| 1| let expectedURLStrings = ["www.google.com", "±"] 68| 1| let jsonDictionary = ["urls": expectedURLStrings] 69| 1| 70| 1| expectDecodingError(reason: .conversionFailure, keyPath: "urls") { 71| 1| let _ : [URL] = try jsonDictionary.json(atKeyPath: "urls", invalidItemBehaviour: .fail) 72| 1| } 73| 1| } 74| | 75| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Mocks/MockChild.swift: 1| |// 2| |// MockChild.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 15/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| |@testable import JSONUtilities 11| | 12| |struct MockChild { 13| | let string: String 14| | let integer: Int 15| | let double: Double 16| | let bool: Bool 17| |} 18| | 19| |extension MockChild: JSONObjectConvertible { 20| 18| init(jsonDictionary: JSONDictionary) throws { 21| 18| string = try jsonDictionary.json(atKeyPath: "stringKey") 22| 18| integer = try jsonDictionary.json(atKeyPath: "integerKey") 23| 18| double = try jsonDictionary.json(atKeyPath: "doubleKey") 24| 18| bool = try jsonDictionary.json(atKeyPath: "boolKey") 25| 18| } 26| |} 27| | 28| |// MARK: Extensions necessary for testing 29| | 30| |extension MockChild: Equatable {} 31| | 32| 10|func == (lhs: MockChild, rhs: MockChild) -> Bool { 33| 10| return lhs.string == rhs.string && 34| 10| lhs.integer == rhs.integer && 35| 10| lhs.double == rhs.double && 36| 10| lhs.bool == rhs.bool 37| 10|} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Mocks/MockParent.swift: 1| |// 2| |// MockParent.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 15/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| |@testable import JSONUtilities 11| | 12| |private let randomKey = "asdfghj" 13| | 14| |struct MockParent { 15| | 16| | enum MockEnum: String { 17| | case one 18| | case two 19| | } 20| | 21| | // MARK: JSON raw types and custom objects properties 22| | let mandatoryString: String 23| | let mandatoryInt: Int 24| | let mandatoryDouble: Double 25| | let mandatoryBool: Bool 26| | let mandatoryWeakDictionaryKey: JSONDictionary 27| | let mandatoryCustomJSONObject: MockChild 28| | let mandatoryEnum: MockEnum 29| | 30| | let optionalExistingString: String? 31| | let optionalExistingInt: Int? 32| | let optionalExistingDouble: Double? 33| | let optionalExistingBool: Bool? 34| | let optionalExistingWeakDictionaryKey: JSONDictionary? 35| | let optionalExistingCustomJSONObject: MockChild? 36| | let optionalExistingEnum: MockEnum? 37| | 38| | let optionalMissingString: String? 39| | let optionalMissingInt: Int? 40| | let optionalMissingDouble: Double? 41| | let optionalMissingBool: Bool? 42| | let optionalMissingWeakDictionaryKey: JSONDictionary? 43| | let optionalMissingCustomJSONObject: MockChild? 44| | let optionalMissingEnum: MockEnum? 45| | 46| | // MARK: Array properties 47| | let mandatoryArrayString: [String] 48| | let mandatoryArrayInt: [Int] 49| | let mandatoryArrayDouble: [Double] 50| | let mandatoryArrayBool: [Bool] 51| | let mandatoryWeakDictionaryArrayKey: [JSONDictionary] 52| | let mandatoryArrayCustomJSONObject: [MockChild] 53| | 54| | let optionalExistingArrayString: [String]? 55| | let optionalExistingArrayInt: [Int]? 56| | let optionalExistingArrayDouble: [Double]? 57| | let optionalExistingArrayBool: [Bool]? 58| | let optionalExistingWeakDictionaryArrayKey: [JSONDictionary]? 59| | let optionalExistingArrayCustomJSONObject: [MockChild]? 60| | 61| | let optionalMissingArrayString: [String]? 62| | let optionalMissingArrayInt: [Int]? 63| | let optionalMissingArrayDouble: [Double]? 64| | let optionalMissingArrayBool: [Bool]? 65| | let optionalMissingWeakDictionaryArrayKey: [JSONDictionary]? 66| | let optionalMissingArrayCustomJSONObject: [MockChild]? 67| | 68| | let mandatoryIntDictionary: [String: Int] 69| | let mandatoryObjectDictionary: [String: MockChild] 70| | let mandatoryURLDictionary: [String: URL] 71| | let optionalIntDictionary: [String: Int]? 72| | let optionalObjectDictionary: [String: MockChild]? 73| | let optionalURLDictionary: [String: URL]? 74| | 75| 5| init(jsonDictionary: JSONDictionary) throws { 76| 5| mandatoryString = try jsonDictionary.json(atKeyPath: "keypath.mandatoryStringKey") 77| 5| mandatoryInt = try jsonDictionary.json(atKeyPath: "keypath.mandatoryIntKey") 78| 5| mandatoryDouble = try jsonDictionary.json(atKeyPath: "keypath.mandatoryDoubleKey") 79| 5| mandatoryBool = try jsonDictionary.json(atKeyPath: "keypath.mandatoryBoolKey") 80| 5| mandatoryWeakDictionaryKey = try jsonDictionary.json(atKeyPath: "keypath.mandatoryCustomJSONObjectKey") 81| 5| mandatoryCustomJSONObject = try jsonDictionary.json(atKeyPath: "keypath.mandatoryCustomJSONObjectKey") 82| 5| mandatoryEnum = try jsonDictionary.json(atKeyPath: "keypath.mandatoryEnum") 83| 5| 84| 5| optionalExistingString = jsonDictionary.json(atKeyPath: "keypath.mandatoryStringKey") 85| 5| optionalExistingInt = jsonDictionary.json(atKeyPath: "keypath.mandatoryIntKey") 86| 5| optionalExistingDouble = jsonDictionary.json(atKeyPath: "keypath.mandatoryDoubleKey") 87| 5| optionalExistingBool = jsonDictionary.json(atKeyPath: "keypath.mandatoryBoolKey") 88| 5| optionalExistingWeakDictionaryKey = jsonDictionary.json(atKeyPath: "keypath.mandatoryCustomJSONObjectKey") 89| 5| optionalExistingCustomJSONObject = jsonDictionary.json(atKeyPath: "keypath.mandatoryCustomJSONObjectKey") 90| 5| optionalExistingEnum = jsonDictionary.json(atKeyPath: "keypath.mandatoryEnum") 91| 5| 92| 5| optionalMissingString = jsonDictionary.json(atKeyPath: randomKey) 93| 5| optionalMissingInt = jsonDictionary.json(atKeyPath: randomKey) 94| 5| optionalMissingDouble = jsonDictionary.json(atKeyPath: randomKey) 95| 5| optionalMissingBool = jsonDictionary.json(atKeyPath: randomKey) 96| 5| optionalMissingWeakDictionaryKey = jsonDictionary.json(atKeyPath: randomKey) 97| 5| optionalMissingCustomJSONObject = jsonDictionary.json(atKeyPath: randomKey) 98| 5| optionalMissingEnum = jsonDictionary.json(atKeyPath: randomKey) 99| 5| 100| 5| mandatoryArrayString = try jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayStringKey") 101| 5| mandatoryArrayInt = try jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayIntKey") 102| 5| mandatoryArrayDouble = try jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayDoubleKey") 103| 5| mandatoryArrayBool = try jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayBoolKey") 104| 5| mandatoryWeakDictionaryArrayKey = try jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayCustomJSONObjectKey") 105| 5| mandatoryArrayCustomJSONObject = try jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayCustomJSONObjectKey") 106| 5| 107| 5| optionalExistingArrayString = jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayStringKey") 108| 5| optionalExistingArrayInt = jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayIntKey") 109| 5| optionalExistingArrayDouble = jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayDoubleKey") 110| 5| optionalExistingArrayBool = jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayBoolKey") 111| 5| optionalExistingWeakDictionaryArrayKey = jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayCustomJSONObjectKey") 112| 5| optionalExistingArrayCustomJSONObject = jsonDictionary.json(atKeyPath: "keypath.mandatoryArrayCustomJSONObjectKey") 113| 5| 114| 5| optionalMissingArrayString = jsonDictionary.json(atKeyPath: randomKey) 115| 5| optionalMissingArrayInt = jsonDictionary.json(atKeyPath: randomKey) 116| 5| optionalMissingArrayDouble = jsonDictionary.json(atKeyPath: randomKey) 117| 5| optionalMissingArrayBool = jsonDictionary.json(atKeyPath: randomKey) 118| 5| optionalMissingWeakDictionaryArrayKey = jsonDictionary.json(atKeyPath: randomKey) 119| 5| optionalMissingArrayCustomJSONObject = jsonDictionary.json(atKeyPath: randomKey) 120| 5| 121| 5| mandatoryIntDictionary = try jsonDictionary.json(atKeyPath: "keypath.mandatoryIntDictionary") 122| 5| mandatoryObjectDictionary = try jsonDictionary.json(atKeyPath: "keypath.mandatoryObjectDictionary") 123| 5| mandatoryURLDictionary = try jsonDictionary.json(atKeyPath: "keypath.mandatoryURLDictionary") 124| 5| 125| 5| optionalIntDictionary = jsonDictionary.json(atKeyPath: "keypath.mandatoryIntDictionary") 126| 5| optionalObjectDictionary = jsonDictionary.json(atKeyPath: "keypath.mandatoryObjectDictionary") 127| 5| optionalURLDictionary = jsonDictionary.json(atKeyPath: "keypath.mandatoryURLDictionary") 128| 5| } 129| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Mocks/MockSimpleChild.swift: 1| |// 2| |// MockSimpleChild.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 15/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| |@testable import JSONUtilities 11| | 12| |struct MockSimpleChild { 13| | let name: String 14| |} 15| | 16| |extension MockSimpleChild: JSONObjectConvertible { 17| | 18| 9| init(jsonDictionary: JSONDictionary) throws { 19| 9| name = try jsonDictionary.json(atKeyPath: "name") 20| 9| } 21| | 22| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/Mocks/MockSimpleParent.swift: 1| |// 2| |// MockSimpleParent.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 15/05/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| |@testable import JSONUtilities 11| | 12| |struct MockSimpleParent { 13| | let children: [MockSimpleChild] 14| |} 15| | 16| |extension MockSimpleParent: JSONObjectConvertible { 17| | 18| 1| init(jsonDictionary: JSONDictionary) throws { 19| 1| children = try jsonDictionary.json(atKeyPath: "children") 20| 1| } 21| | 22| |} /Users/travis/build/lucianomarisi/JSONUtilities/Tests/JSONUtilitiesTests/TestFiles/JSONFiles.swift: 1| |// 2| |// JSONFiles.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 21/11/2015. 6| |// Copyright © 2015 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |struct JSONFilename { 12| | static let correct = "correct" 13| | static let missing = "missing" 14| |} 15| | 16| |struct JSONFilePath { 17| | static let correct = "correct".filePath 18| | static let empty = "empty".filePath 19| | static let correctWithoutNested = "correct_without_nested_object".filePath 20| | static let correctWithoutNestedArray = "correct_with_missing_nested_array".filePath 21| | static let correctWithoutRawArray = "correct_with_missing_raw_array".filePath 22| | static let missing = "missing".filePath 23| | static let invalid = "invalid".filePath 24| | static let rootArray = "root_array".filePath 25| |} 26| | 27| |private extension String { 28| 8| var filePath: URL { 29| 8| let parentPath = (#file).components(separatedBy: "/").dropLast().joined(separator: "/") 30| 8| return URL(string: "file://\(parentPath)/\(self).json")! 31| 8| } 32| |} <<<<<< EOF # path=JSONUtilities.framework.coverage.txt /Users/travis/build/lucianomarisi/JSONUtilities/Sources/JSONUtilities/DecodingError.swift: 1| |// 2| |// Errors.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 05/03/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |/** 12| | Error that occurs when decoding 13| | */ 14| |public struct DecodingError: Error, CustomStringConvertible, CustomDebugStringConvertible { 15| | 16| | /// The reason the error occurred 17| | public var reason: Reason 18| | 19| | /// dictionary in which the error occured 20| | public let dictionary: [AnyHashable: Any] 21| | 22| | /// array in which the error occurred 23| | public let array: JSONArray? 24| | 25| | /// the keypath at which the error occurred 26| | public let keyPath: String 27| | 28| | /// the expected type that was being decoded while the error happened 29| | public let expectedType: Any 30| | 31| | /// the value that caused the error 32| | public let value: Any 33| | 34| 117| public init(dictionary: [AnyHashable: Any], keyPath: StringProtocol, expectedType: Any.Type, value: Any, array: JSONArray? = nil, reason: Reason) { 35| 117| self.dictionary = dictionary 36| 117| self.keyPath = keyPath.string 37| 117| self.expectedType = expectedType 38| 117| self.value = value 39| 117| self.reason = reason 40| 117| self.array = array 41| 117| } 42| | 43| 8| var reasonDescription: String { 44| 8| switch reason { 45| 8| case .keyNotFound: 46| 2| return "Nothing found" 47| 8| case .incorrectRawRepresentableRawValue: 48| 2| return "Incorrect rawValue of \(value) for \(expectedType)" 49| 8| case .incorrectType: 50| 2| return "Incorrect type, expected \(expectedType) found \(value)" 51| 8| case .conversionFailure: 52| 2| return "\(expectedType) failed to convert \(value)" 53| 8| } 54| 8| } 55| | 56| 8| public var description: String { 57| 8| return "Decoding failed at \"\(keyPath)\": \(reasonDescription)" 58| 8| } 59| | 60| 4| public var debugDescription: String { 61| 4| return "\(description):\n\(array?.description ?? dictionary.description)" 62| 4| } 63| | 64| | /// The reason the error occurred 65| | public enum Reason: String, CustomStringConvertible { 66| | 67| | /// Key was not found in a JSONDictionary 68| | case keyNotFound = "Key not found" 69| | 70| | /// A value was found that can't initialise a RawRepresentable. In the case of an enum, the rawValue did not match any of the case's rawValue 71| | case incorrectRawRepresentableRawValue = "Incorrect RawRepresentable RawValue" 72| | 73| | /// The value has the incorrect type 74| | case incorrectType = "Incorrect type" 75| | 76| | /// A JSONPrimitiveConvertible failed to convert 77| | case conversionFailure = "Conversion failure" 78| | 79| 4| public var description: String { 80| 4| return rawValue 81| 4| } 82| | } 83| |} /Users/travis/build/lucianomarisi/JSONUtilities/Sources/JSONUtilities/Dictionary+JSONKey.swift: 1| |// 2| |// Dictionary+JSONKey.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 05/03/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |/// Protocol used for defining the valid JSON types, i.e. Int, Double, Float, String and Bool 12| |public protocol JSONRawType {} 13| |extension Int: JSONRawType {} 14| |extension Double: JSONRawType {} 15| |extension Float: JSONRawType {} 16| |extension String: JSONRawType {} 17| |extension Bool: JSONRawType {} 18| | 19| |// Simple protocol used to extend a JSONDictionary 20| |public protocol StringProtocol { 21| | func components(separatedBy: String) -> [String] 22| | var string: String { get } 23| |} 24| |extension String: StringProtocol { 25| 117| public var string: String { 26| 117| return self 27| 117| } 28| |} 29| | 30| |extension Dictionary where Key: StringProtocol { 31| | 32| | // MARK: JSONRawType type 33| | 34| | /// Decode a mandatory JSON raw type 35| 147| public func json(atKeyPath keyPath: Key) throws -> T { 36| 147| return try getValue(atKeyPath: keyPath) 37| 147| } 38| | 39| | /// Decode an optional JSON raw type 40| 34| public func json(atKeyPath keyPath: Key) -> T? { 41| 34| return try? json(atKeyPath: keyPath) as T 42| 34| } 43| | 44| | // MARK: [JSONRawType] type 45| | 46| | /// Decode an Array of mandatory JSON raw types 47| 44| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [T] { 48| 44| return try decodeArray(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour, decode: getValue) 49| 44| } 50| | 51| | /// Decode an Array of optional JSON raw types 52| 18| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [T]? { 53| 18| return try? self.json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [T] 54| 18| } 55| | 56| | // MARK: [String: Any] type 57| | 58| | /// Decodes as a raw Dictionary with a mandatory key 59| 27| public func json(atKeyPath keyPath: Key) throws -> JSONDictionary { 60| 27| return try getValue(atKeyPath: keyPath) 61| 27| } 62| | 63| | /// Decodes as a raw dictionary with an optional key 64| 8| public func json(atKeyPath keyPath: Key) -> JSONDictionary? { 65| 8| return self[keyPath: keyPath] as? JSONDictionary 66| 8| } 67| | 68| | // MARK: [[String: Any]] type 69| | 70| | /// Decodes as a raw dictionary array with a mandatory key 71| 8| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [JSONDictionary] { 72| 8| return try decodeArray(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour, decode: getValue) 73| 8| } 74| | 75| | /// Decodes as a raw ictionary array with an optional key 76| 4| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [JSONDictionary]? { 77| 4| return try? self.json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [JSONDictionary] 78| 4| } 79| | 80| | // MARK: [String: JSONObjectConvertible] type 81| | 82| | /// Decodes a mandatory dictionary 83| 6| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [String: T] { 84| 15| return try decodeDictionary(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { jsonDictionary, key in 85| 15| return try jsonDictionary.json(atKeyPath: key) as T 86| 15| } 87| 6| } 88| | 89| | /// Decodes an optional dictionary 90| 1| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [String: T]? { 91| 1| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [String: T] 92| 1| } 93| | 94| | // MARK: [String: JSONRawType] type 95| | 96| | /// Decodes a mandatory dictionary 97| 7| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [String: T] { 98| 15| return try decodeDictionary(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { jsonDictionary, key in 99| 15| return try jsonDictionary.json(atKeyPath: key) as T 100| 15| } 101| 7| } 102| | 103| | /// Decodes an optional dictionary 104| 1| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [String: T]? { 105| 1| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [String: T] 106| 1| } 107| | 108| | // MARK: [String: JSONPrimitiveConvertible] type 109| | 110| | /// Decodes a mandatory dictionary 111| 6| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [String: T] { 112| 15| return try decodeDictionary(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { jsonDictionary, key in 113| 15| return try jsonDictionary.json(atKeyPath: key) as T 114| 15| } 115| 6| } 116| | 117| | /// Decodes an optional dictionary 118| 1| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [String: T]? { 119| 1| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [String: T] 120| 1| } 121| | 122| | // MARK: Decodable types 123| | 124| | /// Decode a mandatory Decodable object 125| 18| public func json(atKeyPath keyPath: Key) throws -> T { 126| 18| return try T(jsonDictionary: JSONDictionaryForKey(atKeyPath: keyPath)) 127| 18| } 128| | 129| | /// Decode an optional Decodable object 130| 6| public func json(atKeyPath keyPath: Key) -> T? { 131| 6| return try? T(jsonDictionary: JSONDictionaryForKey(atKeyPath: keyPath)) 132| 6| } 133| | 134| | // MARK: [Decodable] types 135| | 136| | /// Decode an Array of mandatory Decodable objects 137| 8| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [T] { 138| 17| return try decodeArray(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { keyPath, jsonArray, value in 139| 17| let jsonDictionary: JSONDictionary = try getValue(atKeyPath: keyPath, array: jsonArray, value: value) 140| 17| return try T(jsonDictionary: jsonDictionary) 141| 17| } 142| 8| } 143| | 144| | /// Decode an Array of optional Decodable objects 145| 2| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [T]? { 146| 2| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [T] 147| 2| } 148| | 149| | // MARK: RawRepresentable type 150| | 151| | /// Decode a mandatory RawRepresentable 152| 12| public func json(atKeyPath keyPath: Key) throws -> T where T.RawValue: JSONRawType { 153| 12| let rawValue: T.RawValue = try getValue(atKeyPath: keyPath) 154| 12| 155| 12| guard let value = T(rawValue: rawValue) else { 156| 1| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: T.self, value: rawValue, reason: .incorrectRawRepresentableRawValue) 157| 11| } 158| 11| 159| 11| return value 160| 12| } 161| | 162| | /// Decode an optional RawRepresentable 163| 6| public func json(atKeyPath keyPath: Key) -> T? where T.RawValue: JSONRawType { 164| 6| return try? json(atKeyPath: keyPath) as T 165| 6| } 166| | 167| | // MARK: [RawRepresentable] type 168| | 169| | /// Decode an array of custom RawRepresentable types with a mandatory key 170| 3| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [T] where T.RawValue: JSONRawType { 171| 3| 172| 6| return try decodeArray(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { keyPath, jsonArray, value in 173| 6| let rawValue: T.RawValue = try getValue(atKeyPath: keyPath, array: jsonArray, value: value) 174| 6| 175| 6| guard let value = T(rawValue: rawValue) else { 176| 2| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: T.self, value: rawValue, array: jsonArray, reason: .incorrectRawRepresentableRawValue) 177| 4| } 178| 4| return value 179| 6| } 180| 3| } 181| | 182| | /// Optionally decode an array of RawRepresentable types with a mandatory key 183| 2| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [T]? where T.RawValue: JSONRawType { 184| 2| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [T] 185| 2| } 186| | 187| | // MARK: [String: RawRepresentable] type 188| | 189| | /// Decode a dictionary of custom RawRepresentable types with a mandatory key 190| 2| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [String: T] where T.RawValue: JSONRawType { 191| 6| return try decodeDictionary(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { jsonDictionary, key in 192| 6| let rawValue: T.RawValue = try jsonDictionary.getValue(atKeyPath: key) 193| 6| 194| 6| guard let value = T(rawValue: rawValue) else { 195| 2| throw DecodingError(dictionary: jsonDictionary, keyPath: keyPath, expectedType: T.self, value: rawValue, reason: .incorrectRawRepresentableRawValue) 196| 4| } 197| 4| return value 198| 6| } 199| 2| } 200| | 201| | /// Optionally decode a dictionary of RawRepresentable types with a mandatory key 202| 1| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [String: T]? where T.RawValue: JSONRawType { 203| 1| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [String: T] 204| 1| } 205| | 206| | // MARK: JSONPrimitiveConvertible type 207| | 208| | /// Decode a custom raw types with a mandatory key 209| 21| public func json(atKeyPath keyPath: Key) throws -> T { 210| 21| let jsonValue: T.JSONType = try getValue(atKeyPath: keyPath) 211| 21| 212| 21| guard let transformedValue = T.from(jsonValue: jsonValue) else { 213| 1| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: T.self, value: jsonValue, reason: .conversionFailure) 214| 20| } 215| 20| 216| 20| return transformedValue 217| 21| } 218| | 219| | /// Optionally decode a custom raw types with a mandatory key 220| 3| public func json(atKeyPath keyPath: Key) -> T? { 221| 3| return try? json(atKeyPath: keyPath) as T 222| 3| } 223| | 224| | // MARK: [JSONPrimitiveConvertible] type 225| | 226| | /// Decode an array of custom raw types with a mandatory key 227| 8| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) throws -> [T] { 228| 15| return try decodeArray(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) { keyPath, jsonArray, value in 229| 15| let jsonValue: T.JSONType = try getValue(atKeyPath: keyPath, array: jsonArray, value: value) 230| 15| 231| 15| guard let transformedValue = T.from(jsonValue: jsonValue) else { 232| 1| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: T.self, value: jsonValue, array: jsonArray, reason: .conversionFailure) 233| 14| } 234| 14| return transformedValue 235| 15| } 236| 8| } 237| | 238| | /// Optionally decode an array custom raw types with a mandatory key 239| 2| public func json(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove) -> [T]? { 240| 2| return try? json(atKeyPath: keyPath, invalidItemBehaviour: invalidItemBehaviour) as [T] 241| 2| } 242| | 243| | // MARK: JSONDictionary and JSONArray creation 244| | 245| 24| fileprivate func JSONDictionaryForKey(atKeyPath keyPath: Key) throws -> JSONDictionary { 246| 24| return try getValue(atKeyPath: keyPath) 247| 24| } 248| | 249| 71| fileprivate func JSONArrayForKey(atKeyPath keyPath: Key) throws -> JSONArray { 250| 71| return try getValue(atKeyPath: keyPath) 251| 71| } 252| | 253| | // MARK: Value decoding 254| | 255| 102| fileprivate func getValue(atKeyPath keyPath: Key, array: [A], value: A) throws -> B { 256| 102| guard let typedValue = value as? B else { 257| 24| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: B.self, value: value, array: array, reason: .incorrectType) 258| 78| } 259| 78| return typedValue 260| 102| } 261| | 262| 308| fileprivate func getValue(atKeyPath keyPath: Key) throws -> T { 263| 308| guard let value = self[keyPath: keyPath] else { 264| 62| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: T.self, value: "", reason: .keyNotFound) 265| 246| } 266| 246| guard let typedValue = value as? T else { 267| 20| throw DecodingError(dictionary: self, keyPath: keyPath, expectedType: T.self, value: value, reason: .incorrectType) 268| 226| } 269| 226| return typedValue 270| 246| } 271| | 272| | // MARK: Dictionary decoding 273| | 274| 21| fileprivate func decodeDictionary(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove, decode: (JSONDictionary, String) throws -> T) throws -> [String: T] { 275| 21| let jsonDictionary: JSONDictionary = try json(atKeyPath: keyPath) 276| 21| 277| 21| var dictionary: [String: T] = [:] 278| 48| for (key, _) in jsonDictionary { 279| 51| if let item = try invalidItemBehaviour.decodeItem(decode: { try decode(jsonDictionary, key) }) { 280| 34| dictionary[key] = item 281| 48| } 282| 48| } 283| 21| 284| 21| return dictionary 285| 21| } 286| | 287| | // MARK: Array decoding 288| | 289| 71| fileprivate func decodeArray(atKeyPath keyPath: Key, invalidItemBehaviour: InvalidItemBehaviour = .remove, decode: (Key, JSONArray, Any) throws -> T) throws -> [T] { 290| 71| let jsonArray = try JSONArrayForKey(atKeyPath: keyPath) 291| 94| return try jsonArray.compactMap { value in 292| 102| try invalidItemBehaviour.decodeItem(decode: { try decode(keyPath, jsonArray, value) }) 293| 94| } 294| 71| } 295| | 296| |} /Users/travis/build/lucianomarisi/JSONUtilities/Sources/JSONUtilities/Dictionary+KeyPath.swift: 1| |// 2| |// Dictionary+KeyPath.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 04/09/2016. 6| |// Copyright © 2016 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |extension Dictionary { 12| | 13| | /// Retrieves a value for a keyPath on the dictionary 14| | /// 15| | /// - parameter keyPath: A string of keys separated by dots 16| | /// 17| | /// - returns: Optionally returns a generic value for this keyPath or nil 18| 404| subscript(keyPath keyPath: StringProtocol) -> Any? { 19| 404| var keys = keyPath.components(separatedBy: ".") 20| 404| guard let firstKey = keys.first as? Key, 21| 404| let value = self[firstKey] 22| 404| else { return nil } 23| 337| 24| 337| keys.removeFirst() 25| 337| 26| 337| if !keys.isEmpty, let subDictionary = value as? [Key: Any] { 27| 81| let rejoined = keys.joined(separator: ".") 28| 81| return subDictionary[keyPath: rejoined] 29| 256| } 30| 256| return value 31| 337| } 32| | 33| |} /Users/travis/build/lucianomarisi/JSONUtilities/Sources/JSONUtilities/InvalidItemBehaviour.swift: 1| |// 2| |// InvalidItemBehaviour.swift 3| |// JSONUtilities 4| |// 5| |// Created by Yonas Kolb on 27/4/17. 6| |// Copyright © 2017 Luciano Marisi. All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |/// The behaviour of what should be done when an invalid JSON object or primitive is found 12| |/// 13| |/// - remove: The item is filtered, only valid items are returned 14| |/// - fail: The call fails. For non optional properties this will throw an error, and for optional properties nil is returned 15| |public enum InvalidItemBehaviour { 16| | case remove 17| | case fail 18| | case value(T) 19| | case custom((DecodingError) -> InvalidItemBehaviour) 20| | 21| 153| func decodeItem(decode: () throws -> T) throws -> T? { 22| 153| do { 23| 153| return try decode() 24| 153| } catch { 25| 52| let decodingError = error as? DecodingError 26| 52| 27| 52| switch self { 28| 52| case .remove: 29| 20| return nil 30| 52| case .fail: 31| 8| throw error 32| 52| case .value(let value): 33| 13| return value 34| 52| case .custom(let getBehaviour): 35| 11| guard let decodingError = decodingError else { return nil } 36| 11| let behaviour = getBehaviour(decodingError) 37| 11| return try behaviour.decodeItem(decode: decode) 38| 52| } 39| 18.4E| } 40| 18.4E| } 41| |} /Users/travis/build/lucianomarisi/JSONUtilities/Sources/JSONUtilities/JSONFileLoading.swift: 1| |// 2| |// JSONFileLoading.swift 3| |// JSONUtilities 4| |// 5| |// Created by Luciano Marisi on 21/11/2015. 6| |// Copyright © 2015 Luciano Marisi All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |public typealias JSONDictionary = [String: Any] 12| |public typealias JSONArray = [Any] 13| | 14| |/** 15| | Loading .json file error 16| | 17| | - FileLoadingFailed: The .json file URL could not be found or the file could not be loaded 18| | - FileDeserializationFailed: NSJSONSerialization failed to deserialize the file 19| | - FileNotAJSONDictionary: The .json does not contain a JSON object (i.e [String: Any]) as a top level object 20| | */ 21| |public enum JSONUtilsError: Error { 22| | case couldNotFindFile 23| | case fileLoadingFailed 24| | case fileDeserializationFailed 25| | case fileNotAJSONDictionary 26| |} 27| | 28| |public extension Dictionary where Key: StringProtocol, Value: Any { 29| | 30| | /** 31| | Load a JSONDictionary from a file 32| | 33| | - parameter filename: The filename of the JSON file 34| | 35| | - throws: Throws if a JSONDictionary cannot be created from the file 36| | 37| | - returns: An initilized JSONDictionary 38| | */ 39| 2| static func from(filename: String, bundle: Bundle = .main) throws -> JSONDictionary { 40| 2| bundle.url(forResource: filename, withExtension: "json") 41| 2| guard let url = bundle.url(forResource: filename, withExtension: "json") else { 42| 1| throw JSONUtilsError.couldNotFindFile 43| 1| } 44| 1| return try from(url: url) 45| 2| } 46| | 47| | /** 48| | Load a JSONDictionary from a file 49| | 50| | - parameter url: The url of the json file 51| | - parameter bundle: The NSBundle to be used 52| | 53| | - throws: Throws if a JSONDictionary cannot be created from the file 54| | 55| | - returns: An initilized JSONDictionary 56| | */ 57| | @discardableResult 58| 10| static func from(url: URL) throws -> JSONDictionary { 59| 10| guard let jsonData = try? Data(contentsOf: url) else { 60| 1| throw JSONUtilsError.fileLoadingFailed 61| 9| } 62| 9| return try from(jsonData: jsonData) 63| 10| } 64| | 65| | /** 66| | Load a JSONDictionary from a NSData object 67| | 68| | - parameter jsonData: The JSON NSData to deserialize 69| | 70| | - throws: Throws if a JSONDictionary cannot be created from the file 71| | 72| | - returns: An initilized JSONDictionary 73| | */ 74| 9| static func from(jsonData: Data) throws -> JSONDictionary { 75| 9| guard let deserializedJSON = try? JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions.mutableContainers) else { 76| 1| throw JSONUtilsError.fileDeserializationFailed 77| 8| } 78| 8| 79| 8| guard let jsonDictionary: JSONDictionary = deserializedJSON as? JSONDictionary else { 80| 1| throw JSONUtilsError.fileNotAJSONDictionary 81| 7| } 82| 7| return jsonDictionary 83| 8| } 84| |} /Users/travis/build/lucianomarisi/JSONUtilities/Sources/JSONUtilities/URL+JSONPrimitiveConvertible.swift: 1| |// 2| |// URL+JSONPrimitiveConvertible.swift 3| |// JSONUtilities 4| |// 5| |// Created by Sam Dods on 14/09/2016. 6| |// Copyright © 2016 Luciano Marisi. All rights reserved. 7| |// 8| | 9| |import Foundation 10| | 11| |extension URL: JSONPrimitiveConvertible { 12| | 13| | public typealias JSONType = String 14| | 15| | /// Creates a URL from a string, in order to conform to JSONPrimitiveConvertible 16| | /// 17| | /// - parameter jsonValue: The string representation of a valid URL 18| | /// 19| | /// - returns: Returns a URL if the input string was successfully converted to a URL 20| 21| public static func from(jsonValue: String) -> URL? { 21| 21| return URL(string: jsonValue) 22| 21| } 23| | 24| |} <<<<<< EOF # path=fixes ./Metadata/JSONUtilities.h:8,10,13,16,18,19 <<<<<< EOF