From 8d28b86605cdb2d91ff8e5159cbd6070a24f1312 Mon Sep 17 00:00:00 2001 From: Ben Wetherfield Date: Sun, 17 Nov 2019 12:21:04 -0800 Subject: [PATCH] Fix Decoding of Arrays of Empty Elements (#152) ## Overview Fixes #123 and extends #145 to accommodate decoding of arrays of empty strings or mixed arrays of non-empty and empty strings. ## Example We may now decode ```xml ``` into the following type ```swift struct EmptyArray: Equatable, Codable { enum CodingKeys: String, CodingKey { case empties = "empty" } let empties: [Empty] } ``` where ```swift struct Empty: Equatable, Codable { } ``` We can also decode a value of the following type ```swift struct EmptyWrapper { let empty: Empty } ``` from ```xml ``` Further, following from #145 we can decode arrays of empty strings ```xml My neighbors are empty ``` as ```swift struct EmptyArray: Equatable, Codable { enum CodingKeys: String, CodingKey { case stringType = "string-type" } let stringType: [String] } ``` and variants. ## Source Compatibility In cases where we decode an array of type `[String?]`, an empty element is now decoded as `""` rather than `nil`, the justification being that `String` can itself take empty values. We use the convention that `nil` signifies the absence of an element (if 0 or 1 of the element are allowed) as in the updated [BreakfastTest](https://github.com/bwetherfield/XMLCoder/blob/0d20614e47df98d1a10174e992c585edf629c9b9/Tests/XMLCoderTests/BreakfastTest.swift) and in the following error-throwing [test case](https://github.com/MaxDesiatov/XMLCoder/blob/2855777ff868e8a4c1d944c7da0ddb49a8b3893e/Tests/XMLCoderTests/Minimal/NullTests.swift#L56-L68). * Add nested choice unkeyed container decoding test * Fix nested unkeyed container implementation for nested keyed box * Clean up unwrapping syntax * Treat corner case of empty string value intrinsic * Remove xcodeproj junk * Add some logging to assess where we're at * Add cases for empty string as null element decoding * Swiftformat * Transform precondition to where clause in switch statement * Remove print statements * Add failing test for a nested array of empty-string value intrinsic elements * Do a little cleanup of single keyed box passing around * Refactor XMLKeyedDecodingContainer.decodeConcrete elements massaging * Remove xcscheme junk * Add fix for empty arrays, wrapped empties etc * Clean up * Revert singleKeyed dive change * Accommodate singleKeyed reading options * Alter Border Test * Add test case that returns [nil] (exists a non-optional property) * Eliminate possibly empty Int from Breakfast test * Fix formatting * Fix forcecast * Fix formatting * Update LinuxMain * Fix tests such that null elements read as empty strings * Fix linux main * Add nested array of empty strings decoding in the explicit style * Add mixed case empty and non-empty string cases * Reinstate missing test * Add test for decoding a null element into an optional type --- .../Decoder/XMLDecoderImplementation.swift | 12 +- .../Decoder/XMLKeyedDecodingContainer.swift | 23 +-- .../Decoder/XMLUnkeyedDecodingContainer.swift | 12 +- Tests/XMLCoderTests/BorderTest.swift | 30 ++++ Tests/XMLCoderTests/BreakfastTest.swift | 1 - Tests/XMLCoderTests/EmptyArrayTest.swift | 68 +++++++++ .../EmptyElementEmptyStringTests.swift | 137 ++++++++++++++++++ Tests/XMLCoderTests/Minimal/NullTests.swift | 14 ++ .../XMLCoderTests/Minimal/OptionalTests.swift | 9 +- .../XMLCoderTests/Minimal/UnkeyedTests.swift | 4 +- Tests/XMLCoderTests/RJITest.swift | 6 +- Tests/XMLCoderTests/XCTestManifests.swift | 20 +++ XMLCoder.xcodeproj/project.pbxproj | 7 + 13 files changed, 315 insertions(+), 28 deletions(-) create mode 100644 Tests/XMLCoderTests/EmptyArrayTest.swift diff --git a/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift b/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift index 04fdb023..4d87901e 100644 --- a/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift +++ b/Sources/XMLCoder/Decoder/XMLDecoderImplementation.swift @@ -319,10 +319,16 @@ extension XMLDecoderImplementation { } func unbox(_ box: Box) throws -> String { - let stringBox: StringBox = try typedBox(box, for: String.self) - let string = stringBox.unboxed + do { + let stringBox: StringBox = try typedBox(box, for: String.self) + return stringBox.unboxed + } catch { + if box is NullBox { + return "" + } + } - return string + return "" } func unbox(_ box: Box) throws -> Date { diff --git a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift index 3a0fb39c..7f115097 100644 --- a/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift @@ -79,8 +79,8 @@ struct XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol { let box = elements.first ?? attributes.first - if let singleKeyed = box as? SingleKeyedBox { - return singleKeyed.element.isNull + if box is SingleKeyedBox { + return false } return box?.isNull ?? true @@ -160,14 +160,19 @@ struct XMLKeyedDecodingContainer: KeyedDecodingContainerProtocol { decoder.codingPath.append(key) defer { decoder.codingPath.removeLast() } - let elements = container.withShared { keyedBox in - keyedBox.elements[key.stringValue] - } + let elements = container.unboxed.elements[key.stringValue] - return XMLUnkeyedDecodingContainer( - referencing: decoder, - wrapping: SharedBox(elements) - ) + if let containsKeyed = elements as? [KeyedBox], let keyed = containsKeyed.first { + return XMLUnkeyedDecodingContainer( + referencing: decoder, + wrapping: SharedBox(keyed.elements.map(SingleKeyedBox.init)) + ) + } else { + return XMLUnkeyedDecodingContainer( + referencing: decoder, + wrapping: SharedBox(elements) + ) + } } public func superDecoder() throws -> Decoder { diff --git a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift index 513843e1..71300449 100644 --- a/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift +++ b/Sources/XMLCoder/Decoder/XMLUnkeyedDecodingContainer.swift @@ -105,11 +105,15 @@ struct XMLUnkeyedDecodingContainer: UnkeyedDecodingContainer { var value: T? if let singleKeyed = box as? SingleKeyedBox { do { - // Drill down to the element in the case of an nested unkeyed element - value = try decode(decoder, singleKeyed.element) + value = try decode(decoder, singleKeyed) } catch { - // Specialize for choice elements - value = try decode(decoder, ChoiceBox(key: singleKeyed.key, element: singleKeyed.element)) + do { + // Drill down to the element in the case of an nested unkeyed element + value = try decode(decoder, singleKeyed.element) + } catch { + // Specialize for choice elements + value = try decode(decoder, ChoiceBox(key: singleKeyed.key, element: singleKeyed.element)) + } } } else { value = try decode(decoder, box) diff --git a/Tests/XMLCoderTests/BorderTest.swift b/Tests/XMLCoderTests/BorderTest.swift index 5dc55ad8..472c72a7 100644 --- a/Tests/XMLCoderTests/BorderTest.swift +++ b/Tests/XMLCoderTests/BorderTest.swift @@ -24,6 +24,16 @@ struct Borders: Codable, Equatable { } } +struct LeftBorders: Codable, Equatable { + let items: [LeftBorder?] + let count: Int + + enum CodingKeys: String, CodingKey { + case items = "border" + case count + } +} + struct Border: Codable, Equatable { struct Value: Codable, Equatable { let style: String? @@ -48,10 +58,30 @@ struct Border: Codable, Equatable { } } +struct LeftBorder: Codable, Equatable { + struct Value: Codable, Equatable { + let style: String? + } + + var left: Value + var right: Value? + var top: Value? + var bottom: Value? + var diagonal: Value? + var horizontal: Value? + var vertical: Value? +} + final class BorderTest: XCTestCase { func testSingleEmpty() throws { let result = try XMLDecoder().decode(Borders.self, from: xml) XCTAssertEqual(result.count, 1) + XCTAssertEqual(result.items[0], Border()) + } + + func testLeftBorder() throws { + let result = try XMLDecoder().decode(LeftBorders.self, from: xml) + XCTAssertEqual(result.count, 1) XCTAssertEqual(result.items[0], nil) } } diff --git a/Tests/XMLCoderTests/BreakfastTest.swift b/Tests/XMLCoderTests/BreakfastTest.swift index 5b5b5cb6..80c6fe24 100644 --- a/Tests/XMLCoderTests/BreakfastTest.swift +++ b/Tests/XMLCoderTests/BreakfastTest.swift @@ -16,7 +16,6 @@ private let xml = """ Belgian Waffles $5.95 Two of our famous Belgian Waffles with plenty of real maple syrup - Strawberry Belgian Waffles diff --git a/Tests/XMLCoderTests/EmptyArrayTest.swift b/Tests/XMLCoderTests/EmptyArrayTest.swift new file mode 100644 index 00000000..8c80d680 --- /dev/null +++ b/Tests/XMLCoderTests/EmptyArrayTest.swift @@ -0,0 +1,68 @@ +// +// EmptyArrayTest.swift +// XMLCoderTests +// +// Created by Benjamin Wetherfield on 10/1/19. +// + +import XCTest +@testable import XMLCoder + +struct Empty: Equatable, Codable {} + +struct EmptyArray: Equatable, Codable { + enum CodingKeys: String, CodingKey { case empties = "empty" } + let empties: [Empty] +} + +struct EmptyWrapper: Equatable, Codable { + let empty: Empty +} + +struct OptionalEmptyWrapper: Equatable, Codable { + let empty: Empty? +} + +private let xml = """ + + + + + +""" + +private let xmlArray = """ + + + + + +""" + +private let xmlContainsEmpty = """ + + + +""" + +class EmptyArrayTest: XCTestCase { + func testEmptyArrayDecode() throws { + let decoded = try XMLDecoder().decode([Empty].self, from: xml.data(using: .utf8)!) + XCTAssertEqual(decoded, [Empty(), Empty(), Empty()]) + } + + func testWrappedEmptyArrayDecode() throws { + let decoded = try XMLDecoder().decode(EmptyArray.self, from: xmlArray.data(using: .utf8)!) + XCTAssertEqual(decoded, EmptyArray(empties: [Empty(), Empty(), Empty()])) + } + + func testWrappedEmptyDecode() throws { + let decoded = try XMLDecoder().decode(EmptyWrapper.self, from: xmlContainsEmpty.data(using: .utf8)!) + XCTAssertEqual(decoded, EmptyWrapper(empty: Empty())) + } + + func testWrappedOptionalEmptyDecode() throws { + let decoded = try XMLDecoder().decode(OptionalEmptyWrapper.self, from: xmlContainsEmpty.data(using: .utf8)!) + XCTAssertEqual(decoded, OptionalEmptyWrapper(empty: Empty())) + } +} diff --git a/Tests/XMLCoderTests/EmptyElementEmptyStringTests.swift b/Tests/XMLCoderTests/EmptyElementEmptyStringTests.swift index 554663f1..26e71525 100644 --- a/Tests/XMLCoderTests/EmptyElementEmptyStringTests.swift +++ b/Tests/XMLCoderTests/EmptyElementEmptyStringTests.swift @@ -9,6 +9,42 @@ import XCTest import XMLCoder class EmptyElementEmptyStringTests: XCTestCase { + struct ExplicitNestingContainer: Equatable, Decodable { + let things: ContainedArray + + struct ContainedArray: Equatable, Decodable { + let thing: [Thing] + + init(_ things: [Thing]) { + thing = things + } + } + } + + struct NestingContainer: Equatable, Decodable { + let things: [Thing] + + enum CodingKeys: String, CodingKey { + case things + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + + var things = [Thing]() + if var thingContainer = try? container.nestedUnkeyedContainer(forKey: .things) { + while !thingContainer.isAtEnd { + things.append(try thingContainer.decode(Thing.self)) + } + } + self.things = things + } + + init(things: [Thing]) { + self.things = things + } + } + struct Parent: Equatable, Codable { let thing: Thing } @@ -58,6 +94,23 @@ class EmptyElementEmptyStringTests: XCTestCase { XCTAssertEqual(expected, result) } + func testArrayOfSomeEmptyElementStringDecoding() throws { + let xml = """ + + + Non-Empty! + Non-Empty! + + """ + let expected = [ + Thing(attribute: nil, value: ""), + Thing(attribute: "x", value: "Non-Empty!"), + Thing(attribute: nil, value: "Non-Empty!"), + ] + let result = try XMLDecoder().decode([Thing].self, from: xml.data(using: .utf8)!) + XCTAssertEqual(expected, result) + } + func testNestedEmptyElementEmptyStringDecoding() throws { let xml = """ @@ -68,4 +121,88 @@ class EmptyElementEmptyStringTests: XCTestCase { let result = try XMLDecoder().decode(Parent.self, from: xml.data(using: .utf8)!) XCTAssertEqual(expected, result) } + + func testExplicitlyNestedArrayOfEmptyElementEmptyStringDecoding() throws { + let xml = """ + + + + + + + + """ + let expected = ExplicitNestingContainer( + things: .init([ + Thing(attribute: nil, value: ""), + Thing(attribute: "x", value: ""), + Thing(attribute: nil, value: ""), + ]) + ) + let result = try XMLDecoder().decode(ExplicitNestingContainer.self, from: xml.data(using: .utf8)!) + XCTAssertEqual(expected, result) + } + + func testExplicitlyNestedArrayOfSomeEmptyElementEmptyStringDecoding() throws { + let xml = """ + + + + Non-Empty! + Non-Empty! + + + """ + let expected = ExplicitNestingContainer( + things: .init([ + Thing(attribute: nil, value: ""), + Thing(attribute: "x", value: "Non-Empty!"), + Thing(attribute: nil, value: "Non-Empty!"), + ]) + ) + let result = try XMLDecoder().decode(ExplicitNestingContainer.self, from: xml.data(using: .utf8)!) + XCTAssertEqual(expected, result) + } + + func testNestedArrayOfEmptyElementEmptyStringDecoding() throws { + let xml = """ + + + + + + + + """ + let expected = NestingContainer( + things: [ + Thing(attribute: nil, value: ""), + Thing(attribute: "x", value: ""), + Thing(attribute: nil, value: ""), + ] + ) + let result = try XMLDecoder().decode(NestingContainer.self, from: xml.data(using: .utf8)!) + XCTAssertEqual(expected, result) + } + + func testNestedArrayOfSomeEmptyElementEmptyStringDecoding() throws { + let xml = """ + + + + Non-Empty! + Non-Empty! + + + """ + let expected = NestingContainer( + things: [ + Thing(attribute: nil, value: ""), + Thing(attribute: "x", value: "Non-Empty!"), + Thing(attribute: nil, value: "Non-Empty!"), + ] + ) + let result = try XMLDecoder().decode(NestingContainer.self, from: xml.data(using: .utf8)!) + XCTAssertEqual(expected, result) + } } diff --git a/Tests/XMLCoderTests/Minimal/NullTests.swift b/Tests/XMLCoderTests/Minimal/NullTests.swift index 8cb84308..872feeb5 100644 --- a/Tests/XMLCoderTests/Minimal/NullTests.swift +++ b/Tests/XMLCoderTests/Minimal/NullTests.swift @@ -52,4 +52,18 @@ class NullTests: XCTestCase { let encoded = try encoder.encode(decoded, withRootKey: "container") XCTAssertEqual(String(data: encoded, encoding: .utf8)!, xmlString) } + + func testNullElement() { + let decoder = XMLDecoder() + + let xmlString = + """ + + + + """ + let xmlData = xmlString.data(using: .utf8)! + + XCTAssertThrowsError(try decoder.decode(Container.self, from: xmlData)) + } } diff --git a/Tests/XMLCoderTests/Minimal/OptionalTests.swift b/Tests/XMLCoderTests/Minimal/OptionalTests.swift index d16af2c7..31120d82 100644 --- a/Tests/XMLCoderTests/Minimal/OptionalTests.swift +++ b/Tests/XMLCoderTests/Minimal/OptionalTests.swift @@ -9,7 +9,7 @@ import XCTest @testable import XMLCoder private struct ExpectNonNil: Decodable, Equatable { - var optional: String? + var optional: String? = "" private enum CodingKeys: String, CodingKey { case optional @@ -26,7 +26,7 @@ private struct ExpectNonNil: Decodable, Equatable { } private struct ExpectOptional: Decodable, Equatable { - var optional: String? + var optional: String? = "" private enum CodingKeys: String, CodingKey { case optional @@ -43,7 +43,7 @@ private struct ExpectOptional: Decodable, Equatable { } private struct DecodeIfPresent: Decodable, Equatable { - var optional: String? + var optional: String? = "" private enum CodingKeys: String, CodingKey { case optional @@ -65,9 +65,6 @@ class OptionalTests: XCTestCase { """.data(using: .utf8)! - XCTAssertThrowsError(try decoder.decode(ExpectNonNil.self, - from: xml)) - let decoded1 = try decoder.decode(ExpectOptional.self, from: xml) XCTAssertEqual(decoded1, ExpectOptional()) let decoded2 = try decoder.decode(DecodeIfPresent.self, from: xml) diff --git a/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift b/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift index 7033f1ab..d4f1dab5 100644 --- a/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift +++ b/Tests/XMLCoderTests/Minimal/UnkeyedTests.swift @@ -68,7 +68,7 @@ class UnkeyedTests: XCTestCase { """.data(using: .utf8)! let decoded = try decoder.decode(NestedNilContainer.self, from: xmlData) - XCTAssertEqual(decoded.value, ["test1", nil, "test2"]) + XCTAssertEqual(decoded.value, ["test1", "", "test2"]) } func testNestedNilSingleElement() throws { @@ -81,7 +81,7 @@ class UnkeyedTests: XCTestCase { """.data(using: .utf8)! let decoded = try decoder.decode(NestedNilContainer.self, from: xmlData) - XCTAssertEqual(decoded.value, [nil]) + XCTAssertEqual(decoded.value, [""]) } func testSingleElement() throws { diff --git a/Tests/XMLCoderTests/RJITest.swift b/Tests/XMLCoderTests/RJITest.swift index 15e68ecb..f86d317d 100644 --- a/Tests/XMLCoderTests/RJITest.swift +++ b/Tests/XMLCoderTests/RJITest.swift @@ -80,7 +80,7 @@ private struct Channel: Codable, Equatable { let values = try decoder.container(keyedBy: CodingKeys.self) title = try values.decode(String.self, forKey: .title) link = try values.decode(URL.self, forKey: .link) - description = try values.decodeIfPresent(String.self, forKey: .description) + description = try values.decode(String.self, forKey: .description) language = try values.decode(String.self, forKey: .language) creator = try values.decode(String.self, forKey: .creator) rights = try values.decode(String.self, forKey: .rights) @@ -108,7 +108,7 @@ private struct Item: Codable, Equatable { let guid: URL let enclosure: Enclosure? let description: String - let subject: String? + let subject: String let date: Date let author: String? @@ -128,7 +128,7 @@ private struct Item: Codable, Equatable { guid = try values.decode(URL.self, forKey: .guid) enclosure = try values.decodeIfPresent(Enclosure.self, forKey: .enclosure) description = try values.decode(String.self, forKey: .description) - subject = try values.decodeIfPresent(String.self, forKey: .subject) + subject = try values.decode(String.self, forKey: .subject) date = try values.decode(Date.self, forKey: .date) author = try values.decodeIfPresent(String.self, forKey: .author) } diff --git a/Tests/XMLCoderTests/XCTestManifests.swift b/Tests/XMLCoderTests/XCTestManifests.swift index dc012d82..26fd2943 100644 --- a/Tests/XMLCoderTests/XCTestManifests.swift +++ b/Tests/XMLCoderTests/XCTestManifests.swift @@ -97,6 +97,7 @@ extension BorderTest { // `swift test --generate-linuxmain` // to regenerate. static let __allTests__BorderTest = [ + ("testLeftBorder", testLeftBorder), ("testSingleEmpty", testSingleEmpty), ] } @@ -253,14 +254,31 @@ extension DynamicNodeEncodingTest { ] } +extension EmptyArrayTest { + // DO NOT MODIFY: This is autogenerated, use: + // `swift test --generate-linuxmain` + // to regenerate. + static let __allTests__EmptyArrayTest = [ + ("testEmptyArrayDecode", testEmptyArrayDecode), + ("testWrappedEmptyArrayDecode", testWrappedEmptyArrayDecode), + ("testWrappedEmptyDecode", testWrappedEmptyDecode), + ("testWrappedOptionalEmptyDecode", testWrappedOptionalEmptyDecode), + ] +} + extension EmptyElementEmptyStringTests { // DO NOT MODIFY: This is autogenerated, use: // `swift test --generate-linuxmain` // to regenerate. static let __allTests__EmptyElementEmptyStringTests = [ ("testArrayOfEmptyElementStringDecoding", testArrayOfEmptyElementStringDecoding), + ("testArrayOfSomeEmptyElementStringDecoding", testArrayOfSomeEmptyElementStringDecoding), ("testEmptyElementEmptyStringDecoding", testEmptyElementEmptyStringDecoding), ("testEmptyElementEmptyStringWithAttributeDecoding", testEmptyElementEmptyStringWithAttributeDecoding), + ("testExplicitlyNestedArrayOfEmptyElementEmptyStringDecoding", testExplicitlyNestedArrayOfEmptyElementEmptyStringDecoding), + ("testExplicitlyNestedArrayOfSomeEmptyElementEmptyStringDecoding", testExplicitlyNestedArrayOfSomeEmptyElementEmptyStringDecoding), + ("testNestedArrayOfEmptyElementEmptyStringDecoding", testNestedArrayOfEmptyElementEmptyStringDecoding), + ("testNestedArrayOfSomeEmptyElementEmptyStringDecoding", testNestedArrayOfSomeEmptyElementEmptyStringDecoding), ("testNestedEmptyElementEmptyStringDecoding", testNestedEmptyElementEmptyStringDecoding), ] } @@ -511,6 +529,7 @@ extension NullTests { static let __allTests__NullTests = [ ("testAttribute", testAttribute), ("testElement", testElement), + ("testNullElement", testNullElement), ] } @@ -808,6 +827,7 @@ public func __allTests() -> [XCTestCaseEntry] { testCase(DecodingContainerTests.__allTests__DecodingContainerTests), testCase(DynamicNodeDecodingTest.__allTests__DynamicNodeDecodingTest), testCase(DynamicNodeEncodingTest.__allTests__DynamicNodeEncodingTest), + testCase(EmptyArrayTest.__allTests__EmptyArrayTest), testCase(EmptyElementEmptyStringTests.__allTests__EmptyElementEmptyStringTests), testCase(EmptyTests.__allTests__EmptyTests), testCase(EnumAssociatedValueTestComposite.__allTests__EnumAssociatedValueTestComposite), diff --git a/XMLCoder.xcodeproj/project.pbxproj b/XMLCoder.xcodeproj/project.pbxproj index 6ea0dfe1..322538b5 100644 --- a/XMLCoder.xcodeproj/project.pbxproj +++ b/XMLCoder.xcodeproj/project.pbxproj @@ -22,6 +22,8 @@ /* Begin PBXBuildFile section */ 07E441BA2340F14B00890F46 /* EmptyElementEmptyStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E441B92340F14B00890F46 /* EmptyElementEmptyStringTests.swift */; }; + B54555BC2343F5C1000D4128 /* EmptyArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54555BB2343F5C1000D4128 /* EmptyArrayTest.swift */; }; + B5EA3BB6230F237800D8D69B /* NestedChoiceArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */; }; 4A062D4F2341924E009BCAC1 /* CombineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4A062D4E2341924E009BCAC1 /* CombineTests.swift */; }; B5EA3BB6230F237800D8D69B /* NestedChoiceArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */; }; B5F74472233F74E400BBDB15 /* RootLevelAttributeTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F74471233F74E400BBDB15 /* RootLevelAttributeTest.swift */; }; @@ -160,6 +162,8 @@ /* Begin PBXFileReference section */ 07E441B92340F14B00890F46 /* EmptyElementEmptyStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyElementEmptyStringTests.swift; sourceTree = ""; }; + B54555BB2343F5C1000D4128 /* EmptyArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyArrayTest.swift; sourceTree = ""; }; + B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedChoiceArrayTest.swift; sourceTree = ""; }; 4A062D4E2341924E009BCAC1 /* CombineTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineTests.swift; sourceTree = ""; }; B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedChoiceArrayTest.swift; sourceTree = ""; }; B5F74471233F74E400BBDB15 /* RootLevelAttributeTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootLevelAttributeTest.swift; sourceTree = ""; }; @@ -467,6 +471,8 @@ OBJ_125 /* SimpleChoiceTests.swift */, OBJ_126 /* SingleChildTests.swift */, OBJ_127 /* SpacePreserveTest.swift */, + B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */, + B54555BB2343F5C1000D4128 /* EmptyArrayTest.swift */, ); name = XMLCoderTests; path = Tests/XMLCoderTests; @@ -704,6 +710,7 @@ D18FBFB82348FAE500FA4F65 /* QuoteDecodingTest.swift in Sources */, OBJ_216 /* BenchmarkTests.swift in Sources */, OBJ_217 /* BooksTest.swift in Sources */, + B54555BC2343F5C1000D4128 /* EmptyArrayTest.swift in Sources */, OBJ_218 /* BorderTest.swift in Sources */, OBJ_219 /* BoolBoxTests.swift in Sources */, OBJ_220 /* DataBoxTests.swift in Sources */,