Skip to content

Commit b1e944c

Browse files
AlkensoJoannis
andauthored
Fix decoding of types conforming to XMLNodeDecoding (#266)
* Fix case when XMLNodeDecoding wants to decode elementOrAttribute, but value is not specified. Fix error reporting when key not found for element or attribute. * Add extra tests for unkeyed containers --------- Co-authored-by: Joannis Orlandos <[email protected]>
1 parent b15a479 commit b1e944c

File tree

2 files changed

+185
-7
lines changed

2 files changed

+185
-7
lines changed

Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -294,17 +294,17 @@ extension XMLKeyedDecodingContainer {
294294

295295
switch strategy(key) {
296296
case .attribute?:
297-
box = try getAttributeBox(attributes, key)
297+
box = try getAttributeBox(for: type, attributes, key)
298298
case .element?:
299-
box = elements
299+
box = try getElementBox(for: type, elements, key)
300300
case .elementOrAttribute?:
301301
box = try getAttributeOrElementBox(attributes, elements, key)
302302
default:
303303
switch type {
304304
case is XMLAttributeProtocol.Type:
305-
box = try getAttributeBox(attributes, key)
305+
box = try getAttributeBox(for: type, attributes, key)
306306
case is XMLElementProtocol.Type:
307-
box = elements
307+
box = try getElementBox(for: type, elements, key)
308308
default:
309309
box = try getAttributeOrElementBox(attributes, elements, key)
310310
}
@@ -346,9 +346,24 @@ extension XMLKeyedDecodingContainer {
346346
return unwrapped
347347
}
348348

349-
private func getAttributeBox(_ attributes: [KeyedBox.Attribute], _ key: Key) throws -> Box {
350-
let attributeBox = attributes.first ?? NullBox()
351-
return attributeBox
349+
private func getAttributeBox<T: Decodable>(for type: T.Type, _ attributes: [KeyedBox.Attribute], _ key: Key) throws -> Box {
350+
if let box = attributes.first { return box }
351+
if type is AnyOptional.Type || type is XMLOptionalAttributeProtocol.Type { return NullBox() }
352+
353+
throw DecodingError.keyNotFound(key, DecodingError.Context(
354+
codingPath: decoder.codingPath,
355+
debugDescription: "No attribute found for key \(_errorDescription(of: key))."
356+
))
357+
}
358+
359+
private func getElementBox<T: Decodable>(for type: T.Type, _ elements: [KeyedBox.Element], _ key: Key) throws -> Box {
360+
guard elements.isEmpty else { return elements }
361+
if type is AnyOptional.Type || type is XMLDecodableSequence.Type { return elements }
362+
363+
throw DecodingError.keyNotFound(key, DecodingError.Context(
364+
codingPath: decoder.codingPath,
365+
debugDescription: "No element found for key \(_errorDescription(of: key))."
366+
))
352367
}
353368

354369
private func getAttributeOrElementBox(_ attributes: [KeyedBox.Attribute], _ elements: [KeyedBox.Element], _ key: Key) throws -> Box {

Tests/XMLCoderTests/AdvancedFeatures/DynamicNodeDecodingTest.swift

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,167 @@ final class DynamicNodeDecodingTest: XCTestCase {
184184
let test = try decoder.decode(TestStruct.self, from: overlappingKeys)
185185
XCTAssertEqual(test, TestStruct(attribute: 123, element: "StringValue"))
186186
}
187+
188+
struct Foo<T: Decodable>: Decodable {
189+
var nested: T
190+
}
191+
192+
struct ArrayFoo<T: Decodable>: Decodable {
193+
var nested: [T]
194+
}
195+
196+
struct NestedElement: Decodable, DynamicNodeDecoding {
197+
var field1: String
198+
var field2: String
199+
200+
static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding { .element }
201+
}
202+
203+
struct NestedAttribute: Decodable, DynamicNodeDecoding {
204+
var field1: String
205+
var field2: String
206+
207+
static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding { .attribute }
208+
}
209+
210+
struct NestedElementOrAttribute: Decodable, DynamicNodeDecoding {
211+
var field1: String
212+
var field2: String
213+
214+
static func nodeDecoding(for key: CodingKey) -> XMLDecoder.NodeDecoding { .elementOrAttribute }
215+
}
216+
217+
func testGenericKeyedFailsOnMissingValues() {
218+
let failureElementXML =
219+
"""
220+
<Root>
221+
<nested><field1>value_1</field1></nested>
222+
</Root>
223+
"""
224+
225+
let failureAttributeXML =
226+
"""
227+
<Root>
228+
<nested field1="value_1" />
229+
</Root>
230+
"""
231+
232+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedElement>.self, from: Data(failureElementXML.utf8))) {
233+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
234+
}
235+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedAttribute>.self, from: Data(failureElementXML.utf8))) {
236+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
237+
}
238+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedElementOrAttribute>.self, from: Data(failureElementXML.utf8))) {
239+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
240+
}
241+
242+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedElement>.self, from: Data(failureAttributeXML.utf8))) {
243+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
244+
}
245+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedAttribute>.self, from: Data(failureAttributeXML.utf8))) {
246+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
247+
}
248+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedElementOrAttribute>.self, from: Data(failureAttributeXML.utf8))) {
249+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
250+
}
251+
}
252+
253+
func testGenericUnkeyedFailsOnMissingValues() {
254+
let failureAttributeXML =
255+
"""
256+
<Root>
257+
<nested field1="value_1" />
258+
<nested field1="value_1" />
259+
<nested field1="value_1" />
260+
</Root>
261+
"""
262+
263+
let failureElementXML =
264+
"""
265+
<Root>
266+
<nested><field1>value_1</field1></nested>
267+
<nested><field1>value_1</field1></nested>
268+
<nested><field1>value_1</field1></nested>
269+
</Root>
270+
"""
271+
272+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedElement>.self, from: Data(failureAttributeXML.utf8))) {
273+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
274+
}
275+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedAttribute>.self, from: Data(failureAttributeXML.utf8))) {
276+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
277+
}
278+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedElementOrAttribute>.self, from: Data(failureAttributeXML.utf8))) {
279+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
280+
}
281+
282+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedElement>.self, from: Data(failureElementXML.utf8))) {
283+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
284+
}
285+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedAttribute>.self, from: Data(failureElementXML.utf8))) {
286+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
287+
}
288+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedElementOrAttribute>.self, from: Data(failureElementXML.utf8))) {
289+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
290+
}
291+
}
292+
293+
func testGenericKeyedSucceedsWithoutMissingValues() {
294+
let successElementXML =
295+
"""
296+
<Root>
297+
<nested><field1>value_1</field1><field2>value_2</field2></nested>
298+
</Root>
299+
"""
300+
301+
let successAttributeXML =
302+
"""
303+
<Root>
304+
<nested field1="value_1" field2="value_2" />
305+
</Root>
306+
"""
307+
308+
XCTAssertNoThrow(try XMLDecoder().decode(Foo<NestedElement>.self, from: Data(successElementXML.utf8)))
309+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedAttribute>.self, from: Data(successElementXML.utf8))) {
310+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
311+
}
312+
XCTAssertNoThrow(try XMLDecoder().decode(Foo<NestedElementOrAttribute>.self, from: Data(successElementXML.utf8)))
313+
314+
XCTAssertThrowsError(try XMLDecoder().decode(Foo<NestedElement>.self, from: Data(successAttributeXML.utf8))) {
315+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
316+
}
317+
XCTAssertNoThrow(try XMLDecoder().decode(Foo<NestedAttribute>.self, from: Data(successAttributeXML.utf8)))
318+
XCTAssertNoThrow(try XMLDecoder().decode(Foo<NestedElementOrAttribute>.self, from: Data(successAttributeXML.utf8)))
319+
}
320+
321+
func testGenericUnkeyedSucceedsWithoutMissingValues() {
322+
let successElementXML =
323+
"""
324+
<Root>
325+
<nested><field1>value_1</field1><field2>value_2</field2></nested>
326+
<nested><field1>value_1</field1><field2>value_2</field2></nested>
327+
</Root>
328+
"""
329+
330+
let successAttributeXML =
331+
"""
332+
<Root>
333+
<nested field1="value_1" field2="value_2" />
334+
<nested field1="value_1" field2="value_2" />
335+
</Root>
336+
"""
337+
338+
XCTAssertNoThrow(try XMLDecoder().decode(ArrayFoo<NestedElement>.self, from: Data(successElementXML.utf8)))
339+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedAttribute>.self, from: Data(successElementXML.utf8))) {
340+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
341+
}
342+
XCTAssertNoThrow(try XMLDecoder().decode(ArrayFoo<NestedElementOrAttribute>.self, from: Data(successElementXML.utf8)))
343+
344+
XCTAssertThrowsError(try XMLDecoder().decode(ArrayFoo<NestedElement>.self, from: Data(successAttributeXML.utf8))) {
345+
guard case DecodingError.keyNotFound = $0 else { XCTFail("Invalid error thrown: \($0)"); return }
346+
}
347+
XCTAssertNoThrow(try XMLDecoder().decode(ArrayFoo<NestedAttribute>.self, from: Data(successAttributeXML.utf8)))
348+
XCTAssertNoThrow(try XMLDecoder().decode(ArrayFoo<NestedElementOrAttribute>.self, from: Data(successAttributeXML.utf8)))
349+
}
187350
}

0 commit comments

Comments
 (0)