diff --git a/Sources/Unbox.swift b/Sources/Unbox.swift index 4cbdc86..cf9f680 100644 --- a/Sources/Unbox.swift +++ b/Sources/Unbox.swift @@ -41,8 +41,8 @@ public func Unbox(dictionary: UnboxableDictionary, context: Any? = } /// Unbox a JSON dictionary into a model `T` beginning at a provided key, optionally using a contextual object. Throws `UnboxError`. -public func Unbox(dictionary: UnboxableDictionary, at key: String, context: Any? = nil) throws -> T { - return try Unboxer.unboxer(at: key, in: dictionary, context: context).performUnboxing() +public func Unbox(dictionary: UnboxableDictionary, at key: String, isKeyPath: Bool = false, context: Any? = nil) throws -> T { + return try Unboxer.unboxer(at: key, in: dictionary, isKeyPath: isKeyPath, context: context).performUnboxing() } /// Unbox an array of JSON dictionaries into an array of `T`, optionally using a contextual object and/or invalid elements. Throws `UnboxError`. @@ -891,9 +891,25 @@ private extension Unboxer { } } - static func unboxer(at key: String, in dictionary: UnboxableDictionary, context: Any?) throws -> Unboxer { - guard let nestedDictionary = dictionary[key] as? UnboxableDictionary else { - throw UnboxValueError.MissingValueForKey(key) + static func unboxer(at key: String, in dictionary: UnboxableDictionary, isKeyPath: Bool, context: Any?) throws -> Unboxer { + var dictionary = dictionary + var modifiedKey = key + + if isKeyPath && key.containsString(".") { + let components = key.componentsSeparatedByString(".") + for i in 0 ..< components.count { + let keyPathComponent = components[i] + + if i == components.count - 1 { + modifiedKey = keyPathComponent + } else if let nestedDictionary = dictionary[keyPathComponent] as? UnboxableDictionary { + dictionary = nestedDictionary + } + } + } + + guard let nestedDictionary = dictionary[modifiedKey] as? UnboxableDictionary else { + throw UnboxValueError.MissingValueForKey(modifiedKey) } return Unboxer(dictionary: nestedDictionary, context: context) diff --git a/Tests/UnboxTests.swift b/Tests/UnboxTests.swift index ebec387..b1157cf 100644 --- a/Tests/UnboxTests.swift +++ b/Tests/UnboxTests.swift @@ -1229,13 +1229,50 @@ class UnboxTests: XCTestCase { func testUnboxingStartingAtMissingCustomKey() { let dictionary: UnboxableDictionary = [ "A": [ - "int": 3 + "int": 14 ] ] do { let unboxed: UnboxTestSimpleMock = try Unbox(dictionary, at: "B") - XCTAssertEqual(unboxed.int, 3) + XCTAssertEqual(unboxed.int, 14) + } catch { + switch error { + case UnboxValueError.MissingValueForKey(let missingKey): + XCTAssertEqual(missingKey, "B") + default: + XCTFail("Unexpected error thrown: \(error)") + } + } + } + + func testUnboxingStartingAtCustomKeyPath() { + let dictionary: UnboxableDictionary = [ + "A": [ + "B": [ + "int": 14 + ] + ] + ] + + do { + let unboxed: UnboxTestSimpleMock = try Unbox(dictionary, at: "A.B", isKeyPath: true) + XCTAssertEqual(unboxed.int, 14) + } catch { + XCTFail("Unexpected error thrown: \(error)") + } + } + + func testUnboxingStartingAtMissingCustomKeyPath() { + let dictionary: UnboxableDictionary = [ + "A": [ + "int": 14 + ] + ] + + do { + let unboxed: UnboxTestSimpleMock = try Unbox(dictionary, at: "A.B", isKeyPath: true) + XCTAssertEqual(unboxed.int, 14) } catch { switch error { case UnboxValueError.MissingValueForKey(let missingKey):