diff --git a/Sources/Unboxer.swift b/Sources/Unboxer.swift index 9e920f1..d0a9fa8 100644 --- a/Sources/Unboxer.swift +++ b/Sources/Unboxer.swift @@ -222,27 +222,28 @@ private extension Unboxer { let value = try self.dictionary[key].orThrow(UnboxPathError.missingKey(key)) return try transform(value).orThrow(UnboxPathError.invalidValue(value, key)) case .keyPath(let keyPath): - var node: UnboxPathNode = self.dictionary let components = keyPath.components(separatedBy: ".") - let lastKey = components.last - for key in components { + guard let lastKey = components.last, !lastKey.isEmpty else { + throw UnboxPathError.emptyKeyPath + } + + let lastPathNode = try components.dropLast().reduce(dictionary) { (node: UnboxPathNode, key: String) -> UnboxPathNode in guard let nextValue = node.unboxPathValue(forKey: key) else { throw UnboxPathError.missingKey(key) } - if key == lastKey { - return try transform(nextValue).orThrow(UnboxPathError.invalidValue(nextValue, key)) - } - guard let nextNode = nextValue as? UnboxPathNode else { throw UnboxPathError.invalidValue(nextValue, key) } - node = nextNode + return nextNode } - throw UnboxPathError.emptyKeyPath + guard let value = lastPathNode.unboxPathValue(forKey: lastKey) else { + throw UnboxPathError.missingKey(lastKey) + } + return try transform(value).orThrow(UnboxPathError.invalidValue(value, lastKey)) } } catch { if let publicError = error as? UnboxError { diff --git a/Tests/UnboxTests/UnboxTests.swift b/Tests/UnboxTests/UnboxTests.swift index 0d0e97f..d3cd6af 100644 --- a/Tests/UnboxTests/UnboxTests.swift +++ b/Tests/UnboxTests/UnboxTests.swift @@ -1744,6 +1744,29 @@ class UnboxTests: XCTestCase { XCTFail("\(error)") } } + + func testKeyPathWithSameLastTwoComponens() { + struct Model: Unboxable { + let message: String + + init(unboxer: Unboxer) throws { + self.message = try unboxer.unbox(keyPath: "message.message") + } + } + + let dictionary: UnboxableDictionary = [ + "message": [ + "message": "value" + ] + ] + + do { + let unboxed: Model = try unbox(dictionary: dictionary) + XCTAssertEqual("value", unboxed.message) + } catch { + XCTFail("\(error)") + } + } } private func UnboxTestDictionaryWithAllRequiredKeysWithValidValues(nested: Bool) -> UnboxableDictionary {