diff --git a/Sources/XMLCoding/Decoder/XMLUnkeyedDecodingContainer.swift b/Sources/XMLCoding/Decoder/XMLUnkeyedDecodingContainer.swift index e7c69d8..a653587 100644 --- a/Sources/XMLCoding/Decoder/XMLUnkeyedDecodingContainer.swift +++ b/Sources/XMLCoding/Decoder/XMLUnkeyedDecodingContainer.swift @@ -37,8 +37,16 @@ internal struct _XMLUnkeyedDecodingContainer : UnkeyedDecodingContainer { case .collapseListUsingItemTag(let itemTag): if container.count == 1, let itemKeyMap = container[0] as? [AnyHashable: Any], - let list = itemKeyMap[itemTag] as? [Any] { - self.container = list + let entry = itemKeyMap[itemTag] { + // if there are multiple items in the list, + // the entry will be a list + if let list = entry as? [Any] { + self.container = list + } else { + // otherwise it will be the singleton entry; + // place it in a list for the container + self.container = [entry] + } } else { self.container = [] } diff --git a/Tests/XMLCodingTests/XMLParsingTests.swift b/Tests/XMLCodingTests/XMLParsingTests.swift index 31b2d68..3184117 100644 --- a/Tests/XMLCodingTests/XMLParsingTests.swift +++ b/Tests/XMLCodingTests/XMLParsingTests.swift @@ -18,6 +18,17 @@ let LIST_XML = """ """ +let SINGLETON_LIST_XML = """ + + + + + id1 + + + + """ + class XMLParsingTests: XCTestCase { struct Result: Codable { let message: String? @@ -130,6 +141,22 @@ class XMLParsingTests: XCTestCase { XCTAssertEqual(LIST_XML, encodedString) } + func testSingletonListDecodingWithDefaultStrategy() throws { + guard let inputData = SINGLETON_LIST_XML.data(using: .utf8) else { + return XCTFail() + } + + let response = try XMLDecoder().decode(ResponseWithList.self, from: inputData) + + XCTAssertEqual(1, response.metadataList.items.count) + + // encode the output to make sure we get what we started with + let data = try XMLEncoder().encode(response, withRootKey: "Response") + let encodedString = String(data: data, encoding: .utf8) ?? "" + + XCTAssertEqual(SINGLETON_LIST_XML, encodedString) + } + func testListDecodingWithCollapseItemTagStrategy() throws { guard let inputData = LIST_XML.data(using: .utf8) else { return XCTFail() @@ -150,11 +177,33 @@ class XMLParsingTests: XCTestCase { XCTAssertEqual(LIST_XML, encodedString) } + + func testSingletonListDecodingWithCollapseItemTagStrategy() throws { + guard let inputData = SINGLETON_LIST_XML.data(using: .utf8) else { + return XCTFail() + } + + let decoder = XMLDecoder() + decoder.listDecodingStrategy = .collapseListUsingItemTag("item") + let response = try decoder.decode(ResponseWithCollapsedList.self, from: inputData) + + XCTAssertEqual(1, response.metadataList.count) + + let encoder = XMLEncoder() + encoder.listEncodingStrategy = .expandListWithItemTag("item") + + // encode the output to make sure we get what we started with + let data = try encoder.encode(response, withRootKey: "Response") + let encodedString = String(data: data, encoding: .utf8) ?? "" + + XCTAssertEqual(SINGLETON_LIST_XML, encodedString) + } static var allTests = [ ("testEmptyElement", testEmptyElement), ("testEmptyElementNotEffectingPreviousElement", testEmptyElementNotEffectingPreviousElement), ("testListDecodingWithDefaultStrategy", testListDecodingWithDefaultStrategy), + ("testSingletonListDecodingWithDefaultStrategy", testSingletonListDecodingWithDefaultStrategy), ("testListDecodingWithCollapseItemTagStrategy", testListDecodingWithCollapseItemTagStrategy) ] }