Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
0a8a07a
Bool: Support init from y,n,yes,no any case, false,true added upperca…
JoeMatt Jan 31, 2019
ae4d50a
KeyedEncoding: Add new string formatters, capitalized, lowercased, up…
JoeMatt Jan 31, 2019
e65d30b
SharedBoxProtocol: Generalize for any Box inheritance
JoeMatt Jan 31, 2019
99fde81
Remove junk string in BreakfastTest xml
JoeMatt Jan 31, 2019
38b79a2
Element coding, remove empty brackets if element string value is empt…
JoeMatt Jan 31, 2019
0300dd1
Add DynamicNodeEncoding protocol
JoeMatt Jan 31, 2019
14549a6
XMLEncoder: Add both option to value encoding, refactor encoder
JoeMatt Jan 31, 2019
f8c594a
XMLDecoder.XMLDecodingStorage refactor initial value to inline var
JoeMatt Jan 31, 2019
2ee4b03
Clear up most swiftlint warnings
JoeMatt Jan 31, 2019
f5f6f8d
Rename left over values from different branch
JoeMatt Jan 31, 2019
e86be14
test: Add coding / decoding tests to DynamicNodeEncoding
JoeMatt Jan 31, 2019
5e94557
Convrted BooksTest to DynamicNodeEncoding, tests string equality
JoeMatt Jan 31, 2019
39b5999
Swiftfomat corrections
JoeMatt Jan 31, 2019
f5a8578
Add test coverage for String+Extensions
JoeMatt Jan 31, 2019
a110b83
Fix lowercasingFirstLetter was capitalized
JoeMatt Feb 6, 2019
f60bb38
Apply SwiftFormat fix to UnkeyedDecodingContainer
MaxDesiatov Feb 6, 2019
110a66b
Add isEmpty property to KeyedStorage
MaxDesiatov Feb 6, 2019
2061b34
Merge branch 'master' into feature/dynamicNodeEncoding
MaxDesiatov Feb 6, 2019
d29ce8f
Remove redundant extension of DynamicNodeEncoding
MaxDesiatov Feb 9, 2019
32f58ee
Update DynamicNodeEncoding extensions, fix test
MaxDesiatov Feb 9, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add DynamicNodeEncoding protocol
DynamicNodeEncoding allows easily adding the ability to choose if iVars should be attribute or element encoded by inheriting DynamicNodeEncoding and implimenting a single static function in any Codable class or struct.

This is simpler than the current method that requires a global dynamic encoding closure for every XMLEncoder instance. This allows changing the encoding where the data models live, rather than the creator of the XMLEncoder instance needing to have knowledge of all the possible structs and classes that the encoder might encounter at init time.
  • Loading branch information
JoeMatt committed Feb 6, 2019
commit 0300dd1d627547b5a617427a8f574cbacde79a4e
30 changes: 30 additions & 0 deletions Sources/XMLCoder/Encoder/DynamicNodeEncoding.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// DynamicNodeEncoding.swift
// XMLCoder
//
// Created by Joseph Mattiello on 1/24/19.
//

import Foundation

public protocol DynamicNodeEncoding: Encodable {
static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding
}

public extension DynamicNodeEncoding {
static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
return XMLEncoder.NodeEncoding.default
}
}

extension Array: DynamicNodeEncoding where Element: DynamicNodeEncoding {
public static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
return Element.nodeEncoding(forKey: key)
}
}

extension DynamicNodeEncoding where Self: Collection, Self.Iterator.Element: DynamicNodeEncoding {
public static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
return Element.nodeEncoding(forKey: key)
}
}
160 changes: 160 additions & 0 deletions Tests/XMLCoderTests/DynamicNodeEncodingTest.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
//
// SOAPSample.swift
// XMLCoderTests
//
// Created by Joseph Mattiello on 1/23/19.
//

import Foundation
import XCTest
@testable import XMLCoder

let libraryXML = """
<?xml version="1.0" encoding="UTF-8"?>
<library count="2">
<book id="123">
<id>123</id>
<title>Cat in the Hat</title>
<category main="Y">Kids</category>
<category main="N">Wildlife</category>
</book>
<book id="456">
<id>789</id>
<title>1984</title>
<category main="Y">Classics</category>
<category main="N">News</category>
</book>
</library>
""".data(using: .utf8)!

private struct Library: Codable, Equatable {
let count: Int
let books: [Book]

private enum CodingKeys: String, CodingKey {
case count
case books = "book"
}
}

private struct Book: Codable, Equatable, DynamicNodeEncoding {
let id: UInt
let title: String
let categories: [Category]

private enum CodingKeys: String, CodingKey {
case id
case title
case categories = "category"
}

static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
switch key {
case Book.CodingKeys.id: return .both
default: return .element
}
}
}

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

var nested = try container.nestedUnkeyedContainer(forKey: .categories)

var decoded = [Category]()
var finished = false

while !finished {
do {
let another = try nested.decode(Category.self)
decoded.append(another)
} catch DecodingError.valueNotFound {
finished = true
} catch {
throw error
}
}

categories = decoded
}
}

private struct Category: Codable, Equatable, DynamicNodeEncoding {
let main: Bool
let value: String

private enum CodingKeys: String, CodingKey {
case main
case value = ""
}

static func nodeEncoding(forKey key: CodingKey) -> XMLEncoder.NodeEncoding {
switch key {
case Category.CodingKeys.main:
return .attribute
default:
return .element
}
}
}

private func decodeArray<T>(_ decoder: Decoder, decode: (inout UnkeyedDecodingContainer) throws -> T) throws -> [T] {
let keyedContainer = try decoder.container(keyedBy: CodingKeys.self)
var container = try keyedContainer.nestedUnkeyedContainer(forKey: .value)

var decoded = [T]()
var finished = false

while !finished {
do {
decoded.append(try decode(&container))
} catch DecodingError.valueNotFound {
finished = true
} catch {
throw error
}
}

return decoded
}

final class IntrinsicTest: XCTestCase {

func testEncode() {
let book1 = Book(id: 123,
title: "Cat in the Hat",
categories: [
Category(main: true, value: "Kids"),
Category(main: false, value: "Wildlife")
])

let book2 = Book(id: 456,
title: "1984",
categories: [
Category(main: true, value: "Classics"),
Category(main: false, value: "News")
])

let library = Library(count: 2, books: [book1, book2])
let encoder = XMLEncoder()
encoder.outputFormatting = [.prettyPrinted]

let header = XMLHeader(version: 1.0, encoding: "UTF-8")
do {
let encoded = try encoder.encode(library, withRootKey: "library", header: header)
let xmlString = String(data: encoded, encoding: .utf8)
XCTAssertNotNil(xmlString)
print(xmlString!)
} catch {
print("Test threw error: " + error.localizedDescription)
XCTFail(error.localizedDescription)
}
}

static var allTests = [
("testEncode", testEncode),
]
}
8 changes: 8 additions & 0 deletions XMLCoder.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
A61DCCD821DF9CA200C0A19D /* ClassTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61DCCD621DF8DB300C0A19D /* ClassTests.swift */; };
A61FE03921E4D60B0015D993 /* UnkeyedIntTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61FE03721E4D4F10015D993 /* UnkeyedIntTests.swift */; };
A61FE03C21E4EAB10015D993 /* KeyedIntTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A61FE03A21E4EA8B0015D993 /* KeyedIntTests.swift */; };
B35157CE21F986DD009CA0CC /* DynamicNodeEncoding.swift in Sources */ = {isa = PBXBuildFile; fileRef = B35157CD21F986DD009CA0CC /* DynamicNodeEncoding.swift */; };
B3BE1D612202C1F600259831 /* DynamicNodeEncodingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */; };
BF63EF0021CCDED2001D38C5 /* XMLStackParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EEFF21CCDED2001D38C5 /* XMLStackParserTests.swift */; };
BF63EF0621CD7A74001D38C5 /* URLBox.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0521CD7A74001D38C5 /* URLBox.swift */; };
BF63EF0821CD7AF8001D38C5 /* URLBoxTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */; };
Expand Down Expand Up @@ -126,6 +128,8 @@
A61DCCD621DF8DB300C0A19D /* ClassTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClassTests.swift; sourceTree = "<group>"; };
A61FE03721E4D4F10015D993 /* UnkeyedIntTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnkeyedIntTests.swift; sourceTree = "<group>"; };
A61FE03A21E4EA8B0015D993 /* KeyedIntTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedIntTests.swift; sourceTree = "<group>"; };
B35157CD21F986DD009CA0CC /* DynamicNodeEncoding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicNodeEncoding.swift; sourceTree = "<group>"; };
B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicNodeEncodingTest.swift; sourceTree = "<group>"; };
BF63EEFF21CCDED2001D38C5 /* XMLStackParserTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = XMLStackParserTests.swift; sourceTree = "<group>"; };
BF63EF0521CD7A74001D38C5 /* URLBox.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLBox.swift; sourceTree = "<group>"; };
BF63EF0721CD7AF8001D38C5 /* URLBoxTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLBoxTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -329,6 +333,7 @@
OBJ_19 /* XMLKeyedEncodingContainer.swift */,
OBJ_20 /* XMLReferencingEncoder.swift */,
OBJ_21 /* XMLUnkeyedEncodingContainer.swift */,
B35157CD21F986DD009CA0CC /* DynamicNodeEncoding.swift */,
);
path = Encoder;
sourceTree = "<group>";
Expand Down Expand Up @@ -360,6 +365,7 @@
OBJ_38 /* RelationshipsTest.swift */,
BF63EF1D21CEC99A001D38C5 /* BenchmarkTests.swift */,
D1FC040421C7EF8200065B43 /* RJISample.swift */,
B3BE1D602202C1F600259831 /* DynamicNodeEncodingTest.swift */,
A61DCCD621DF8DB300C0A19D /* ClassTests.swift */,
D14D8A8521F1D6B300B0D31A /* SingleChildTests.swift */,
);
Expand Down Expand Up @@ -549,6 +555,7 @@
OBJ_53 /* EncodingErrorExtension.swift in Sources */,
BF9457B921CBB4DB005ACFDE /* XMLStackParser.swift in Sources */,
OBJ_54 /* XMLEncoder.swift in Sources */,
B35157CE21F986DD009CA0CC /* DynamicNodeEncoding.swift in Sources */,
BF9457BA21CBB4DB005ACFDE /* ISO8601DateFormatter.swift in Sources */,
OBJ_55 /* XMLEncodingStorage.swift in Sources */,
BF9457A921CBB498005ACFDE /* KeyedBox.swift in Sources */,
Expand Down Expand Up @@ -611,6 +618,7 @@
OBJ_85 /* NodeEncodingStrategyTests.swift in Sources */,
OBJ_86 /* NoteTest.swift in Sources */,
BF63EF0C21CD7F28001D38C5 /* EmptyTests.swift in Sources */,
B3BE1D612202C1F600259831 /* DynamicNodeEncodingTest.swift in Sources */,
BF9457F721CBB6BC005ACFDE /* DataTests.swift in Sources */,
BF9457EE21CBB6BC005ACFDE /* IntTests.swift in Sources */,
BF8171F221D3D03E00901EB0 /* SharedBoxTests.swift in Sources */,
Expand Down