Skip to content

Commit e110d12

Browse files
Temporary Fix Until Bridging
1 parent d62c232 commit e110d12

File tree

2 files changed

+133
-125
lines changed

2 files changed

+133
-125
lines changed

Foundation/NSJSONSerialization.swift

Lines changed: 74 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -149,102 +149,114 @@ public class NSJSONSerialization : NSObject {
149149
}
150150
}
151151
// MARK: - Serialization
152-
private extension NSJSONSerialization
153-
{
154-
private class func escapeStringForJSON(string: String) -> String
155-
{
156-
// TODO: Escape control characters \u{0} to \u{20} in string as required by RFC
152+
private extension NSJSONSerialization {
153+
154+
private class func escapeStringForJSON(string: String) -> String {
157155
return NSString(NSString(string).stringByReplacingOccurrencesOfString("\\", withString: "\\\\")).stringByReplacingOccurrencesOfString("\"", withString: "\\\"")
158156
}
159-
private class func serializeArray<T>(array: [T], newIndent: String, newLine: String, space: String, indentation: String) throws -> String
160-
{
157+
private class func serializeArray<T>(array: [T], newIndent: String, newLine: String, space: String, indentation: String) throws -> String {
161158
guard array.count > 0
162-
else
163-
{
159+
else {
164160
return "\(indentation)[]"
165161
}
166162
var index = 0
167163
return try array.reduce("\(indentation)[\(newLine)", combine: {
168-
++index
164+
index += 1
169165
let separator = (index == array.count) ? "\(newLine)\(indentation)]" : ",\(newLine)"
170166
return $0 + (try JSONSerialize($1, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation + newIndent)) + separator
171167
})
172168
}
173169

174-
private class func serializePOD(object: Any) throws -> String
175-
{
176-
if let str = object as? String
177-
{
178-
return "\"\(escapeStringForJSON(str))\""
179-
}
180-
else if let num = object as? Double
181-
{
182-
// FIXME: Use NSNumber once API is fixed
183-
// Detect numeric type and use true, false keywords or integer type if not double
184-
return "\(num)"
185-
}
186-
else if let _ = object as? NSNull
187-
{
188-
return "null"
189-
}
190-
else
191-
{
192-
throw NSCocoaError.PropertyListWriteInvalidError
170+
private class func serializePOD<T: Any>(object: T, indentation: String) throws -> String {
171+
if let str = object as? String {
172+
return indentation + "\"\(escapeStringForJSON(str))\""
173+
} else if let _ = object as? NSNull {
174+
return indentation + "null"
175+
} else if let obj = object as? CustomStringConvertible {
176+
// Using CustomStringConvertible is a hack to allow for all POD types
177+
// Once bridging works we can revert back.
178+
// TODO: Fix when bridging works
179+
return indentation + escapeStringForJSON(obj.description)
180+
}
181+
else if let num = object as? NSNumber {
182+
return num.description
183+
} else {
184+
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.PropertyListWriteInvalidError.rawValue, userInfo: [
185+
"NSDebugDescription" : "Cannot serialize \(object.dynamicType)"
186+
])
193187
}
194188
}
195189

196-
private class func serializeObject<T>(object: [String: T], newIndent: String, newLine: String, space: String, indentation: String) throws -> String
197-
{
190+
private class func serializeObject<T>(object: [String: T], newIndent: String, newLine: String, space: String, indentation: String) throws -> String {
198191
guard object.count > 0
199-
else
200-
{
192+
else {
201193
return "\(indentation){}"
202194
}
203195
var index = 0
204196
return try object.reduce("\(indentation){\(newLine)", combine: {
205197
let valueString : String
206-
do
207-
{
208-
valueString = try serializePOD($1.1)
198+
do {
199+
valueString = try serializePOD($1.1, indentation: "")
209200
}
210-
catch
211-
{
201+
catch {
212202
valueString = try JSONSerialize($1.1, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation + newIndent)
213203
}
214-
++index
204+
index += 1
215205
let separator = (index == object.count) ? "\(newLine)\(indentation)}" : ",\(newLine)"
216206
return $0 + "\(indentation + newIndent)\"" + escapeStringForJSON($1.0) + "\"\(space):\(space)" + valueString + separator
217207
})
218208
}
219209

220-
private class func JSONSerialize(object: Any, newIndent: String, newLine: String, space: String, topLevel: Bool = false, indentation: String = "") throws -> String
221-
{
222-
if let array = object as? [Any]
223-
{
210+
private class func JSONSerialize<T>(object: T, newIndent: String, newLine: String, space: String, topLevel: Bool = false, indentation: String = "") throws -> String {
211+
// TODO: - revisit this once bridging story gets fully figured out
212+
if let array = object as? [Any] {
224213
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
225-
}
226-
else if let dict = object as? [String : Any]
227-
{
214+
} else if let array = object as? NSArray {
215+
return try serializeArray(array.bridge(), newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
216+
} else if let array = object as? [NSNumber] {
217+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
218+
} else if let array = object as? [String] {
219+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
220+
} else if let array = object as? [Double] {
221+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
222+
} else if let array = object as? [Int] {
223+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
224+
} else if let array = object as? [Bool] {
225+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
226+
} else if let array = object as? [Float] {
227+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
228+
} else if let array = object as? [UInt] {
229+
return try serializeArray(array, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
230+
} else if let dict = object as? [String : Any] {
231+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
232+
} else if let dict = object as? NSDictionary {
233+
return try JSONSerialize(dict.bridge(), newIndent: newIndent, newLine: newLine, space: space, topLevel: topLevel, indentation: indentation)
234+
} else if let dict = object as? [String: String] {
235+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
236+
} else if let dict = object as? [String: Double] {
237+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
238+
} else if let dict = object as? [String: Int] {
239+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
240+
} else if let dict = object as? [String: Bool] {
241+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
242+
} else if let dict = object as? [String: Float] {
243+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
244+
} else if let dict = object as? [String: UInt] {
245+
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
246+
} else if let dict = object as? [String: NSNumber] {
228247
return try serializeObject(dict, newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
229248
}
230-
else if let array = object as? NSArray
231-
{
232-
// FIXME: Warns that NSArray can't be casted to [AnyObject]
233-
return try serializeArray(array as! [AnyObject], newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
234-
}
235-
else if let dict = object as? NSDictionary
236-
{
237-
// FIXME: Warns that NSArray can't be casted to [String: AnyObject]
238-
return try serializeObject(dict as! [String: AnyObject], newIndent: newIndent, newLine: newLine, space: space, indentation: indentation)
239-
}
240-
else
241-
{
249+
// This else if situation will greatly improve once bridging works properly.
250+
// For now we check for Floats, Doubles, Int, UInt, Bool and NSNumber
251+
// but this may disallow other types like CGFloat, Int32, etc which is a problem
252+
else {
242253
guard !topLevel
243-
else
244-
{
245-
throw NSCocoaError.PropertyListWriteInvalidError
254+
else {
255+
throw NSError(domain: NSCocoaErrorDomain, code: NSCocoaError.PropertyListWriteInvalidError.rawValue, userInfo: [
256+
"NSDebugDescription" : "Unable to use \(object.dynamicType) as a top level JSON object."
257+
])
246258
}
247-
return try serializePOD(object)
259+
return try serializePOD(object, indentation: indentation)
248260
}
249261
}
250262
}

TestFoundation/TestNSJSONSerialization.swift

Lines changed: 59 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -456,108 +456,104 @@ extension TestNSJSONSerialization {
456456
// MARK: - JSONSerialization
457457
extension TestNSJSONSerialization {
458458

459-
var serializationTests: [(String, () -> ())] {
459+
var serializationTests: [(String, () throws -> ())] {
460460
return [
461461
("test_serialize_emptyObject", test_serialize_emptyObject),
462462
("test_serialize_multiStringObject", test_serialize_multiStringObject),
463463
("test_serialize_complicatedObject", test_serialize_complicatedObject),
464464

465465
("test_serialize_emptyArray", test_serialize_emptyArray),
466-
("test_serialize_multiStringArray", test_serialize_multiPODArray),
466+
("test_serialize_multiPODArray", test_serialize_multiPODArray),
467467

468468
("test_serialize_values", test_serialize_nested),
469469
]
470470
}
471471

472-
func trySerialize(subject: Any) throws -> String
473-
{
474-
let data = try NSJSONSerialization.dataWithJSONObject(subject, options: [])
472+
func trySerializeHelper(subject: Any, options: NSJSONWritingOptions) throws -> String {
473+
let data = try NSJSONSerialization.dataWithJSONObject(subject, options: options)
475474
guard let string = NSString(data: data, encoding: NSUTF8StringEncoding)
476475
else {
477476
XCTFail("Unable to convert data to string")
478477
return ""
479478
}
480-
return string
479+
return string.bridge()
480+
}
481+
482+
func trySerialize(subject: Any) throws -> String {
483+
return try trySerializeHelper(subject, options: [])
484+
}
485+
func trySerializePretty(subject: Any) throws -> String {
486+
return try trySerializeHelper(subject, options: [.PrettyPrinted])
481487
}
482488

483489
//MARK: - Object Serialization
484490
func test_serialize_emptyObject() {
485491

486-
do {
487-
XCTAssertEqual(try trySerialize([String: Any]()), "{}")
488-
XCTAssertEqual(try trySerialize([String: NSNumber]()), "{}")
489-
XCTAssertEqual(try trySerialize([String: String]()), "{}")
490-
var json = [String: Float]()
491-
json["s"] = 1.0
492-
json["s"] = nil
493-
XCTAssertEqual(try trySerialize(json), "{}")
494-
}
495-
catch {
496-
XCTFail("Error thrown: \(error)")
497-
}
492+
XCTAssertEqual(try trySerialize([String: Any]()), "{}")
493+
XCTAssertEqual(try trySerializePretty([String: Any]()), "{}")
494+
495+
XCTAssertEqual(try trySerialize([String: NSNumber]()), "{}")
496+
XCTAssertEqual(try trySerialize([String: String]()), "{}")
497+
498+
var json = [String: Double]()
499+
json["s"] = 1.0
500+
json["s"] = nil
501+
XCTAssertEqual(try trySerialize(json), "{}")
502+
XCTAssertEqual(try trySerializePretty(json), "{}")
498503
}
499504

500505
func test_serialize_multiStringObject() {
501-
do {
502-
var json = [String: String]()
503-
json["hello"] = "world"
504-
XCTAssertEqual(try trySerialize(json), "{\"hello\":\"world\"}")
505-
// testing escaped characters like " and \
506-
json["swift"] = "is \\ \"awesome\""
507-
XCTAssertEqual(try trySerialize(json), "{\"hello\":\"world\",\"swift\":\"is \\\\ \\\"awesome\\\"\"}")
508-
}
509-
catch {
510-
XCTFail("Error thrown: \(error)")
511-
}
506+
507+
var json = [String: String]()
508+
json["hello"] = "world"
509+
XCTAssertEqual(try trySerialize(json), "{\"hello\":\"world\"}")
510+
XCTAssertEqual(try trySerializePretty(json), "{\n\t\"hello\" : \"world\"\n}")
511+
512+
// testing escaped characters like " and \
513+
json["swift"] = "is \\ \"awesome\""
514+
XCTAssertEqual(try trySerialize(json), "{\"hello\":\"world\",\"swift\":\"is \\\\ \\\"awesome\\\"\"}")
515+
XCTAssertEqual(try trySerializePretty(json), "{\n\t\"hello\" : \"world\",\n\t\"swift\" : \"is \\\\ \\\"awesome\\\"\"\n}")
512516
}
513517

514518
func test_serialize_complicatedObject() {
515-
do {
516-
let json = ["a": 4.0, "b": NSNull(), "c": "string", "d": false]
517-
XCTAssertEqual(try trySerialize(json), "{\"b\":null,\"a\":4.0,\"d\":0.0,\"c\":\"string\"}")
518-
}
519-
catch {
520-
XCTFail("Error thrown: \(error)")
521-
}
519+
let json : [String: Any] = ["a": 4.0, "b": NSNull(), "c": "string", "d": false]
520+
XCTAssertEqual(try trySerialize(json), "{\"b\":null,\"a\":4.0,\"d\":false,\"c\":\"string\"}")
521+
XCTAssertEqual(try trySerializePretty(json), "{\n\t\"b\" : null,\n\t\"a\" : 4.0,\n\t\"d\" : false,\n\t\"c\" : \"string\"\n}")
522522
}
523523

524524
//MARK: - Array Deserialization
525525
func test_serialize_emptyArray() {
526-
do {
527-
XCTAssertEqual(try trySerialize([String]()), "[]")
528-
XCTAssertEqual(try trySerialize([NSNumber]()), "[]")
529-
XCTAssertEqual(try trySerialize([Any]()), "[]")
530-
XCTAssertEqual(try trySerialize([AnyObject]()), "[]")
531-
}
532-
catch {
533-
XCTFail("Error thrown: \(error)")
534-
}
526+
527+
XCTAssertEqual(try trySerialize([String]()), "[]")
528+
XCTAssertEqual(try trySerializePretty([String]()), "[]")
529+
530+
XCTAssertEqual(try trySerialize([NSNumber]()), "[]")
531+
XCTAssertEqual(try trySerializePretty([NSNumber]()), "[]")
532+
533+
XCTAssertEqual(try trySerialize([Any]()), "[]")
535534
}
536535

537536
func test_serialize_multiPODArray() {
537+
XCTAssertEqual(try trySerialize(["hello", "swift⚡️"]), "[\"hello\",\"swift⚡️\"]")
538+
XCTAssertEqual(try trySerializePretty(["hello", "swift⚡️"]), "[\n\t\"hello\",\n\t\"swift⚡️\"\n]")
538539

539-
do {
540-
XCTAssertEqual(try trySerialize(["hello", "swift⚡️"]), "[\"hello\",\"swift⚡️\"]")
541-
XCTAssertEqual(try trySerialize([1.0, 3.3, 2.3]), "[1.0,3.3,2.3]")
542-
XCTAssertEqual(try trySerialize([NSNull(), "hello", 1.0]), "[null,\"hello\",1.0]")
543-
}
544-
catch {
545-
XCTFail("Error thrown: \(error)")
546-
}
540+
XCTAssertEqual(try trySerialize([1.0, 3.3, 2.3]), "[1.0,3.3,2.3]")
541+
XCTAssertEqual(try trySerializePretty([1.0, 3.3, 2.3]), "[\n\t1.0,\n\t3.3,\n\t2.3\n]")
542+
543+
544+
let array: [Any] = [NSNull(), "hello", 1.0]
545+
XCTAssertEqual(try trySerialize(array), "[null,\"hello\",1.0]")
546+
XCTAssertEqual(try trySerializePretty(array), "[\n\tnull,\n\t\"hello\",\n\t1.0\n]")
547547
}
548548

549549
//MARK: - Nested Serialization
550550
func test_serialize_nested() {
551-
do {
552-
let first = ["a": 4.0, "b": NSNull(), "c": "string", "d": false]
553-
let second = [NSNull(), "hello", 1.0]
554-
let json = [first, second, NSNull(), "hello", 1.0]
555-
// FIXME: Update decimal points and boolean once using NSNumber
556-
XCTAssertEqual(try trySerialize(json), "[{\"b\":null,\"a\":4.0,\"d\":0.0,\"c\":\"string\"},[null,\"hello\",1.0],null,\"hello\",1.0]")
557-
}
558-
catch {
559-
XCTFail("Error thrown: \(error)")
560-
}
551+
let first: [String: Any] = ["a": 4.0, "b": NSNull(), "c": "string", "d": false]
552+
let second: [Any] = [NSNull(), "hello", 1.0]
553+
let json: [Any] = [first, second, NSNull(), "hello", 1.0]
554+
XCTAssertEqual(try trySerialize(json), "[{\"b\":null,\"a\":4.0,\"d\":false,\"c\":\"string\"},[null,\"hello\",1.0],null,\"hello\",1.0]")
555+
XCTAssertEqual(try trySerializePretty(json), "[\n\t{\n\t\t\"b\" : null,\n\t\t\"a\" : 4.0,\n\t\t\"d\" : false,\n\t\t\"c\" : \"string\"\n\t},\n\t[\n\t\tnull,\n\t\t\"hello\",\n\t\t1.0\n\t],\n\tnull,\n\t\"hello\",\n\t1.0\n]")
556+
561557
}
562558
}
563559

0 commit comments

Comments
 (0)