Skip to content

Commit

Permalink
Merge branch 'fix-nested-choice-array' into wetherfield/nested-array-…
Browse files Browse the repository at this point in the history
…empty-string-test
  • Loading branch information
bwetherfield committed Sep 30, 2019
2 parents d8fca56 + 686a2a3 commit 88167db
Show file tree
Hide file tree
Showing 4 changed files with 153 additions and 8 deletions.
19 changes: 12 additions & 7 deletions Sources/XMLCoder/Decoder/XMLKeyedDecodingContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,14 +160,19 @@ struct XMLKeyedDecodingContainer<K: CodingKey>: 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 {
Expand Down
22 changes: 21 additions & 1 deletion Tests/XMLCoderTests/EmptyElementEmptyStringTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,28 @@ import XMLCoder

class EmptyElementEmptyStringTests: XCTestCase {

struct ContainerMultiple: Equatable, Codable {
struct ContainerMultiple: 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 ContainerSingle: Equatable, Codable {
Expand Down
116 changes: 116 additions & 0 deletions Tests/XMLCoderTests/NestedChoiceArrayTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//
// NestedChoiceArrayTest.swift
// XMLCoder
//
// Created by Benjamin Wetherfield on 8/22/19.
//

import XCTest
@testable import XMLCoder

private struct Book: Decodable {
let title: String
let chapters: [Chapter]

enum CodingKeys: String, CodingKey {
case title
case chapters
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
title = try container.decode(String.self, forKey: .title)

var chapters = [Chapter]()

if var chapterContainer = try? container.nestedUnkeyedContainer(forKey: .chapters) {
while !chapterContainer.isAtEnd {
chapters.append(try chapterContainer.decode(Chapter.self))
}
}

self.chapters = chapters
}

init(title: String, chapters: [Chapter]) {
self.title = title
self.chapters = chapters
}
}

private enum Chapter {
struct Content {
let title: String
let content: String
}

case intro(Content)
case body(Content)
case outro(Content)
}

private enum BookError: Error {
case unknownChapterType
}

extension Chapter: Decodable {
enum CodingKeys: String, XMLChoiceCodingKey {
case intro, body = "chapter", outro
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do {
self = .body(try container.decode(Content.self, forKey: .body))
} catch {
do {
self = .intro(try container.decode(Content.self, forKey: .intro))
} catch {
self = .outro(try container.decode(Content.self, forKey: .outro))
}
}
}
}

extension Chapter.Content: Decodable {
enum CodingKeys: String, CodingKey {
case title
case value = ""
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)

title = try container.decode(String.self, forKey: .title)
content = try container.decode(String.self, forKey: .value)
}
}

extension Book: Equatable {}
extension Chapter: Equatable {}
extension Chapter.Content: Equatable {}

class NestedChoiceArrayTest: XCTestCase {
func testDecodingNestedChoiceArray() throws {
let xml = """
<?xml version="1.0" encoding="UTF-8"?>
<book title="Example">
<chapters>
<intro title="Intro">Content of first chapter</intro>
<chapter title="Chapter 1">Content of chapter 1</chapter>
<chapter title="Chapter 2">Content of chapter 2</chapter>
<outro title="Epilogue">Content of last chapter</outro>
</chapters>
</book>
"""
let decoded = try XMLDecoder().decode(Book.self, from: xml.data(using: .utf8)!)
let expected = Book(title: "Example",
chapters: [
.intro(.init(title: "Intro", content: "Content of first chapter")),
.body(.init(title: "Chapter 1", content: "Content of chapter 1")),
.body(.init(title: "Chapter 2", content: "Content of chapter 2")),
.outro(.init(title: "Epilogue", content: "Content of last chapter")),
])
XCTAssertEqual(decoded, expected)
}
}
4 changes: 4 additions & 0 deletions XMLCoder.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

/* Begin PBXBuildFile section */
07E441BA2340F14B00890F46 /* EmptyElementEmptyStringTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07E441B92340F14B00890F46 /* EmptyElementEmptyStringTests.swift */; };
B5EA3BB6230F237800D8D69B /* NestedChoiceArrayTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */; };
OBJ_148 /* BoolBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_12 /* BoolBox.swift */; };
OBJ_149 /* Box.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_13 /* Box.swift */; };
OBJ_150 /* ChoiceBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = OBJ_14 /* ChoiceBox.swift */; };
Expand Down Expand Up @@ -154,6 +155,7 @@

/* Begin PBXFileReference section */
07E441B92340F14B00890F46 /* EmptyElementEmptyStringTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyElementEmptyStringTests.swift; sourceTree = "<group>"; };
B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NestedChoiceArrayTest.swift; sourceTree = "<group>"; };
OBJ_100 /* DecimalTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecimalTests.swift; sourceTree = "<group>"; };
OBJ_101 /* EmptyTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmptyTests.swift; sourceTree = "<group>"; };
OBJ_102 /* FloatTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -449,6 +451,7 @@
OBJ_125 /* SimpleChoiceTests.swift */,
OBJ_126 /* SingleChildTests.swift */,
OBJ_127 /* SpacePreserveTest.swift */,
B5EA3BB4230F235C00D8D69B /* NestedChoiceArrayTest.swift */,
);
name = XMLCoderTests;
path = Tests/XMLCoderTests;
Expand Down Expand Up @@ -721,6 +724,7 @@
OBJ_253 /* OptionalTests.swift in Sources */,
07E441BA2340F14B00890F46 /* EmptyElementEmptyStringTests.swift in Sources */,
OBJ_254 /* StringTests.swift in Sources */,
B5EA3BB6230F237800D8D69B /* NestedChoiceArrayTest.swift in Sources */,
OBJ_255 /* UIntTests.swift in Sources */,
OBJ_256 /* URLTests.swift in Sources */,
OBJ_257 /* UnkeyedIntTests.swift in Sources */,
Expand Down

0 comments on commit 88167db

Please sign in to comment.