Skip to content

Commit

Permalink
Merge pull request #69 from jgrandelli/optionals-in-arrays
Browse files Browse the repository at this point in the history
Optionals in arrays
  • Loading branch information
jarsen authored Nov 15, 2016
2 parents 4bdc613 + ea906f7 commit 0e81e8e
Show file tree
Hide file tree
Showing 6 changed files with 126 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
.DS_Store

# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
Expand Down
4 changes: 4 additions & 0 deletions Marshal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
459FC6BD1DC28F0000BD9DAC /* TestMissingData.json in Resources */ = {isa = PBXBuildFile; fileRef = 459FC6BC1DC28F0000BD9DAC /* TestMissingData.json */; };
7112BA9B1C7ECD5600B657F9 /* People.json in Resources */ = {isa = PBXBuildFile; fileRef = 7112BA971C7ECD5600B657F9 /* People.json */; };
7112BA9C1C7ECD5600B657F9 /* TestDictionary.json in Resources */ = {isa = PBXBuildFile; fileRef = 7112BA981C7ECD5600B657F9 /* TestDictionary.json */; };
7112BA9D1C7ECD5600B657F9 /* TestObjectArray.json in Resources */ = {isa = PBXBuildFile; fileRef = 7112BA991C7ECD5600B657F9 /* TestObjectArray.json */; };
Expand Down Expand Up @@ -44,6 +45,7 @@
/* End PBXContainerItemProxy section */

/* Begin PBXFileReference section */
459FC6BC1DC28F0000BD9DAC /* TestMissingData.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = TestMissingData.json; sourceTree = "<group>"; };
7112BA971C7ECD5600B657F9 /* People.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = People.json; sourceTree = "<group>"; };
7112BA981C7ECD5600B657F9 /* TestDictionary.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = TestDictionary.json; sourceTree = "<group>"; };
7112BA991C7ECD5600B657F9 /* TestObjectArray.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = TestObjectArray.json; sourceTree = "<group>"; };
Expand Down Expand Up @@ -140,6 +142,7 @@
7112BA991C7ECD5600B657F9 /* TestObjectArray.json */,
7112BA9A1C7ECD5600B657F9 /* TestSimpleArray.json */,
AE5720E61C7FE97E00D45DE3 /* TestSimpleSet.json */,
459FC6BC1DC28F0000BD9DAC /* TestMissingData.json */,
AFBED25E1C7E1AAB00622331 /* MarshalTests.swift */,
AFBED2601C7E1AAB00622331 /* Info.plist */,
71EFC3A71CCB4EAF00394E57 /* PerformanceTestObjects.swift */,
Expand Down Expand Up @@ -253,6 +256,7 @@
AE5720E71C7FE97E00D45DE3 /* TestSimpleSet.json in Resources */,
71EFC3AA1CCB505C00394E57 /* Large.json in Resources */,
7112BA9E1C7ECD5600B657F9 /* TestSimpleArray.json in Resources */,
459FC6BD1DC28F0000BD9DAC /* TestMissingData.json in Resources */,
7112BA9C1C7ECD5600B657F9 /* TestDictionary.json in Resources */,
7112BA9D1C7ECD5600B657F9 /* TestObjectArray.json in Resources */,
);
Expand Down
54 changes: 54 additions & 0 deletions MarshalTests/MarshalTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,49 @@ class MarshalTests: XCTestCase {
}
}

func testArraysWithOptionalObjects() {
guard let path = Bundle(for: type(of: self)).path(forResource: "TestMissingData", ofType: "json"),
let data = try? Data(contentsOf: URL(fileURLWithPath: path)),
let json = try? JSONParser.JSONObjectWithData(data) else {
XCTFail("Error parsing TestMissingData.json")
return
}

// Using normal parsing, if any of the objects fail initialization they all fail
let failedArrayOfCars: [Car]? = try? json.value(for: "cars")
XCTAssertNil(failedArrayOfCars, "failedArrayOfCars should be nil")

let optionalArrayOfOptionalCars: [Car?]? = try? json.value(for: "cars")
XCTAssertNotNil(optionalArrayOfOptionalCars, "optionalArrayOfOptionalCars should not be nil")
XCTAssert(optionalArrayOfOptionalCars?.count == 8, "optionalArrayOfOptionalCars should have 8 objects. Actual count = \(optionalArrayOfOptionalCars?.count)")
XCTAssert(optionalArrayOfOptionalCars?.contains(where: { $0?.make == "Lexus" }) == false, "optionalArrayOfOptionalCars should not contain a Lexus because the Lexus was malformed")
XCTAssert(optionalArrayOfOptionalCars?[1] == nil, "optionalArrayOfOptionalCars[1] should be nil")

do {
let arrayOfOptionalCars: [Car?] = try json.value(for: "cars")

XCTAssertNotNil(arrayOfOptionalCars, "arrayOfOptionalCars should not be nil")
XCTAssert(arrayOfOptionalCars.count == 8, "arrayOfOptionalCars should have 8 objects. Actual count = \(optionalArrayOfOptionalCars?.count)")
XCTAssert(arrayOfOptionalCars.contains(where: { $0?.make == "Lexus" }) == false, "arrayOfOptionalCars should not contain a Lexus because the Lexus was malformed")
XCTAssert(arrayOfOptionalCars[1] == nil, "arrayOfOptionalCars[1] should be nil")
}
catch {
XCTFail("error marshaling arrayOfOptionalCars: \(error)")
}
}

func testDiscardingErrors() {
guard let path = Bundle(for: type(of: self)).path(forResource: "TestMissingData", ofType: "json"),
let data = try? Data(contentsOf: URL(fileURLWithPath: path)),
let json = try? JSONParser.JSONObjectWithData(data) else {
XCTFail("Error parsing TestMissingData.json")
return
}

let arrayOfCarsWithoutInvalidObjects: [Car]? = try? json.value(for: "cars", discardingErrors: true)
XCTAssert(arrayOfCarsWithoutInvalidObjects?.count == 5, "arrayOfCarsWithoutInvalidObjects should have 5 objects. Actual count = \(arrayOfCarsWithoutInvalidObjects?.count)")
XCTAssert(arrayOfCarsWithoutInvalidObjects?.contains(where: { $0.make == "Lexus" }) == false, "arrayOfCarsWithoutInvalidObjects should not contain a Lexus because the Lexus was malformed")
}
}

private struct Address: Unmarshaling {
Expand All @@ -399,6 +442,7 @@ private struct Person: Unmarshaling {
let lastName:String
let score:Int
let address:Address?

init(object json: MarshaledObject) throws {
firstName = try json.value(for: "first")
lastName = try json.value(for: "last")
Expand All @@ -413,3 +457,13 @@ private struct AgedPerson: Unmarshaling {
age = try object.value(for: "age")
}
}

private struct Car: Unmarshaling {
let make: String
let model: String

init(object: MarshaledObject) throws {
make = try object.value(for: "make")
model = try object.value(for: "model")
}
}
12 changes: 12 additions & 0 deletions MarshalTests/TestMissingData.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"cars": [
{"make": "Audi", "model": "A4"},
{"make": null, "model": "None"},
{"make": "Audi", "model": "A7"},
{"make": "BMW", "model": 240},
{"make": "Jeep", "model": "Wrangler"},
{"make": "Lexus"},
{"make": "VW", "model": "CC"},
{"make": "VW", "model": "Tiguan"}
]
}
26 changes: 24 additions & 2 deletions Sources/MarshaledObject.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,17 @@ public extension MarshaledObject {
}
}

public func value<A: ValueType>(for key: KeyType) throws -> [A] {
public func value<A: ValueType>(for key: KeyType, discardingErrors: Bool = false) throws -> [A] {
let any = try self.any(for: key)
do {
return try Array<A>.value(from: any, discardingErrors: discardingErrors)
}
catch let MarshalError.typeMismatch(expected: expected, actual: actual) {
throw MarshalError.typeMismatchWithKey(key: key.stringValue, expected: expected, actual: actual)
}
}

public func value<A: ValueType>(for key: KeyType) throws -> [A?] {
let any = try self.any(for: key)
do {
return try Array<A>.value(from: any)
Expand All @@ -73,8 +83,20 @@ public extension MarshaledObject {
throw MarshalError.typeMismatchWithKey(key: key.stringValue, expected: expected, actual: actual)
}
}

public func value<A: ValueType>(for key: KeyType, discardingErrors: Bool = false) throws -> [A]? {
do {
return try self.value(for: key, discardingErrors: discardingErrors) as [A]
}
catch MarshalError.keyNotFound {
return nil
}
catch MarshalError.nullValue {
return nil
}
}

public func value<A: ValueType>(for key: KeyType) throws -> [A]? {
public func value<A: ValueType>(for key: KeyType) throws -> [A?]? {
do {
return try self.value(for: key) as [A]
}
Expand Down
35 changes: 30 additions & 5 deletions Sources/ValueType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,39 @@ extension Int64: ValueType {
}

extension Array where Element: ValueType {
public static func value(from object: Any) throws -> [Element] {
public static func value(from object: Any, discardingErrors: Bool = false) throws -> [Element] {
guard let anyArray = object as? [AnyObject] else {
throw MarshalError.typeMismatch(expected: self, actual: type(of: object))
}
return try anyArray.map {
let value = try Element.value(from: $0)

if discardingErrors {
return anyArray.flatMap {
let value = try? Element.value(from: $0)
guard let element = value as? Element else {
return nil
}
return element
}
}
else {
return try anyArray.map {
let value = try Element.value(from: $0)
guard let element = value as? Element else {
throw MarshalError.typeMismatch(expected: Element.self, actual: type(of: value))
}
return element
}
}
}

public static func value(from object: Any) throws -> [Element?] {
guard let anyArray = object as? [AnyObject] else {
throw MarshalError.typeMismatch(expected: self, actual: type(of: object))
}
return anyArray.map {
let value = try? Element.value(from: $0)
guard let element = value as? Element else {
throw MarshalError.typeMismatch(expected: Element.self, actual: type(of: value))
return nil
}
return element
}
Expand All @@ -74,7 +99,7 @@ extension Dictionary: ValueType {

extension Set where Element: ValueType {
public static func value(from object: Any) throws -> Set<Element> {
let elementArray = try [Element].value(from: object)
let elementArray: [Element] = try [Element].value(from: object)
return Set<Element>(elementArray)
}
}
Expand Down

0 comments on commit 0e81e8e

Please sign in to comment.