Skip to content

Commit 651c684

Browse files
committed
Merge pull request #10 from eungkyu/master
Pull request to extend the functionality
2 parents 0ed8033 + aa3ed23 commit 651c684

File tree

6 files changed

+119
-6
lines changed

6 files changed

+119
-6
lines changed

Demo/Demo/User.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ struct User {
1616
var company: Company?
1717
var friends: [User] = []
1818
var website: NSURL?
19+
var props: [String: String] = [:]
1920
}
2021

2122
extension User: JSONCodable {
@@ -27,6 +28,7 @@ extension User: JSONCodable {
2728
company = try JSONDictionary.decode("company")
2829
friends = try JSONDictionary.decode("friends")
2930
website = try JSONDictionary.decode("website.url", transformer: JSONTransformers.StringToNSURL)
31+
props = try JSONDictionary.decode("props")
3032
}
3133
catch {
3234
print(error)
@@ -42,6 +44,7 @@ extension User: JSONCodable {
4244
try result.encode(company, key: "company")
4345
try result.encode(friends, key: "friends")
4446
try result.encode(website, key: "website", transformer: JSONTransformers.StringToNSURL)
47+
try result.encode(props, key: "props")
4548
return result
4649
}
4750
}

Demo/Demo/ViewController.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ class ViewController: UIViewController {
2727
["id": 27, "full_name": "Bob Jefferson"],
2828
["id": 29, "full_name": "Jen Jackson"]
2929
],
30-
"website": ["url": "http://johnappleseed.com"]
30+
"website": ["url": "http://johnappleseed.com"],
31+
"props": ["prop a": "value a", "prop b": "value b"]
3132
]
3233

3334
print("Initial JSON:\n\(JSON)\n\n")

JSONCodable/JSONDecodable.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,28 @@ public extension Dictionary where Value: AnyObject {
8282
}
8383
}
8484

85+
// optional array of scalars
86+
public func decode<Value: JSONCompatible>(key: Key) throws -> [String: Value]? {
87+
if let y = get(key) ?? self[key] {
88+
guard let x = y as? [String: Value] else {
89+
throw JSONDecodableError.IncompatibleTypeError(key: key as! String, elementType: y.dynamicType, expectedType: [String: Value].self)
90+
}
91+
return x
92+
}
93+
return nil
94+
}
95+
96+
// required dictionary of scalars
97+
public func decode<Value: JSONCompatible>(key: Key) throws -> [String: Value] {
98+
guard let y = get(key) ?? self[key] else {
99+
throw JSONDecodableError.MissingTypeError(key: key as! String)
100+
}
101+
guard let x = y as? [String: Value] else {
102+
throw JSONDecodableError.IncompatibleTypeError(key: key as! String, elementType: y.dynamicType, expectedType: [String: Value].self)
103+
}
104+
return x
105+
}
106+
85107
// TODO: validate array elements
86108
// optional array of decodables
87109
public func decode<Element: JSONDecodable>(key: Key) throws -> [Element]? {
@@ -130,6 +152,9 @@ public extension Dictionary where Value: AnyObject {
130152
// optional decodable
131153
public func decode<Type: JSONDecodable>(key: Key) throws -> Type? {
132154
if let y = get(key) ?? self[key] {
155+
if y is NSNull {
156+
return nil
157+
}
133158
guard let x = y as? [String : AnyObject] else {
134159
throw JSONDecodableError.DictionaryTypeExpectedError(key: key as! String, elementType: y.dynamicType)
135160
}

JSONCodable/JSONEncodable.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
public enum JSONEncodableError: ErrorType, CustomStringConvertible {
1212
case IncompatibleTypeError(elementType: Any.Type)
1313
case ArrayIncompatibleTypeError(elementType: Any.Type)
14+
case DictionaryIncompatibleTypeError(elementType: Any.Type)
1415
case ChildIncompatibleTypeError(key: String, elementType: Any.Type)
1516
case TransformerFailedError(
1617
key: String
@@ -22,6 +23,8 @@ public enum JSONEncodableError: ErrorType, CustomStringConvertible {
2223
return "JSONEncodableError: Incompatible type \(elementType)"
2324
case let .ArrayIncompatibleTypeError(elementType: elementType):
2425
return "JSONEncodableError: Got an array of incompatible type \(elementType)"
26+
case let .DictionaryIncompatibleTypeError(elementType: elementType):
27+
return "JSONEncodableError: Got an dictionary of incompatible type \(elementType)"
2528
case let .ChildIncompatibleTypeError(key: key, elementType: elementType):
2629
return "JSONEncodableError: Got incompatible type \(elementType) for key \(key)"
2730
case let .TransformerFailedError(key: key):
@@ -77,6 +80,21 @@ public extension Array {//where Element: JSONEncodable {
7780

7881
// Dictionary convenience methods
7982

83+
public extension Dictionary {//where Key: String, Value: JSONEncodable {
84+
public func toJSON() throws -> AnyObject {
85+
var result: [String: AnyObject] = [:]
86+
for (k, item) in self {
87+
if let item = item as? JSONEncodable {
88+
result[String(k)] = try item.toJSON()
89+
}
90+
else {
91+
throw JSONEncodableError.DictionaryIncompatibleTypeError(elementType: item.dynamicType)
92+
}
93+
}
94+
return result
95+
}
96+
}
97+
8098
public extension Dictionary where Value: AnyObject {
8199
public mutating func encode(value: Any, key: Key) throws {
82100
let actualValue: Any
@@ -107,6 +125,15 @@ public extension Dictionary where Value: AnyObject {
107125
self[key] = (result as! Value)
108126
}
109127

128+
// test for dictionary
129+
else if let dict = actualValue as? JSONDictionary {
130+
if dict.dictionaryIsJSONEncodable() {
131+
let encodableDict = dict.dictionaryMadeJSONEncodable()
132+
let result = try encodableDict.toJSON()
133+
self[key] = (result as! Value)
134+
}
135+
}
136+
110137
// incompatible type
111138
else {
112139
throw JSONEncodableError.ChildIncompatibleTypeError(key: key as! String, elementType: actualValue.dynamicType)

JSONCodable/JSONHelpers.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@
66
// Copyright © 2015 matthewcheok. All rights reserved.
77
//
88

9+
// Dictionary handling
10+
11+
protocol JSONDictionary {
12+
func dictionaryIsJSONEncodable() -> Bool
13+
func dictionaryMadeJSONEncodable() -> [String: JSONEncodable]
14+
}
15+
16+
extension Dictionary : JSONDictionary {
17+
func dictionaryIsJSONEncodable() -> Bool {
18+
return Key.self is String.Type && Value.self is JSONEncodable.Type
19+
}
20+
21+
func dictionaryMadeJSONEncodable() -> [String: JSONEncodable] {
22+
var dict: [String: JSONEncodable] = [:]
23+
for (k, v) in self {
24+
dict[String(k)] = v as? JSONEncodable
25+
}
26+
return dict
27+
}
28+
}
29+
930
// Array handling
1031

1132
protocol JSONArray {

JSONCodable/JSONString.swift

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,48 @@ import Foundation
1010

1111
public extension JSONEncodable {
1212
public func toJSONString() throws -> String {
13-
let json = try toJSON()
14-
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions(rawValue: 0))
15-
guard let string = NSString(data: data, encoding: NSUTF8StringEncoding) else {
16-
return ""
13+
switch self {
14+
case let str as String:
15+
return escapeJSONString(str)
16+
case is Bool, is Int, is Float, is Double:
17+
return String(self)
18+
default:
19+
let json = try toJSON()
20+
let data = try NSJSONSerialization.dataWithJSONObject(json, options: NSJSONWritingOptions(rawValue: 0))
21+
guard let string = NSString(data: data, encoding: NSUTF8StringEncoding) else {
22+
return ""
23+
}
24+
return string as String
25+
}
26+
}
27+
}
28+
29+
private func escapeJSONString(str: String) -> String {
30+
var chars = String.CharacterView("\"")
31+
for c in str.characters {
32+
switch c {
33+
case "\\":
34+
chars.append("\\")
35+
chars.append("\\")
36+
case "\"":
37+
chars.append("\\")
38+
chars.append("\"")
39+
default:
40+
chars.append(c)
41+
}
42+
}
43+
chars.append("\"")
44+
return String(chars)
45+
}
46+
47+
public extension Optional where Wrapped: JSONEncodable {
48+
public func toJSONString() throws -> String {
49+
switch self {
50+
case let .Some(jsonEncodable):
51+
return try jsonEncodable.toJSONString()
52+
case nil:
53+
return "null"
1754
}
18-
return string as String
1955
}
2056
}
2157

0 commit comments

Comments
 (0)