| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See http://swift.org/LICENSE.txt for license information |
| // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| |
| |
| #if DEPLOYMENT_RUNTIME_OBJC || os(Linux) |
| import Foundation |
| import XCTest |
| #else |
| import SwiftFoundation |
| import SwiftXCTest |
| #endif |
| |
| |
| class TestNSJSONSerialization : XCTestCase { |
| |
| let supportedEncodings = [ |
| NSUTF8StringEncoding, |
| NSUTF16LittleEndianStringEncoding, NSUTF16BigEndianStringEncoding, |
| NSUTF32LittleEndianStringEncoding, NSUTF32BigEndianStringEncoding |
| ] |
| |
| static var allTests: [(String, TestNSJSONSerialization -> () throws -> Void)] { |
| return JSONObjectWithDataTests |
| + deserializationTests |
| + isValidJSONObjectTests |
| + serializationTests |
| } |
| |
| } |
| |
| //MARK: - JSONObjectWithData |
| extension TestNSJSONSerialization { |
| |
| class var JSONObjectWithDataTests: [(String, TestNSJSONSerialization -> () throws -> Void)] { |
| return [ |
| ("test_JSONObjectWithData_emptyObject", test_JSONObjectWithData_emptyObject), |
| ("test_JSONObjectWithData_encodingDetection", test_JSONObjectWithData_encodingDetection), |
| ] |
| } |
| |
| func test_JSONObjectWithData_emptyObject() { |
| let subject = NSData(bytes: UnsafePointer<Void>([UInt8]([0x7B, 0x7D])), length: 2) |
| |
| let object = try! NSJSONSerialization.JSONObjectWithData(subject, options: []) as? [String:Any] |
| XCTAssertEqual(object?.count, 0) |
| } |
| |
| //MARK: - Encoding Detection |
| func test_JSONObjectWithData_encodingDetection() { |
| let subjects: [(String, [UInt8])] = [ |
| // BOM Detection |
| ("{} UTF-8 w/BOM", [0xEF, 0xBB, 0xBF, 0x7B, 0x7D]), |
| ("{} UTF-16BE w/BOM", [0xFE, 0xFF, 0x0, 0x7B, 0x0, 0x7D]), |
| ("{} UTF-16LE w/BOM", [0xFF, 0xFE, 0x7B, 0x0, 0x7D, 0x0]), |
| ("{} UTF-32BE w/BOM", [0x00, 0x00, 0xFE, 0xFF, 0x0, 0x0, 0x0, 0x7B, 0x0, 0x0, 0x0, 0x7D]), |
| ("{} UTF-32LE w/BOM", [0xFF, 0xFE, 0x00, 0x00, 0x7B, 0x0, 0x0, 0x0, 0x7D, 0x0, 0x0, 0x0]), |
| |
| // RFC4627 Detection |
| ("{} UTF-8", [0x7B, 0x7D]), |
| ("{} UTF-16BE", [0x0, 0x7B, 0x0, 0x7D]), |
| ("{} UTF-16LE", [0x7B, 0x0, 0x7D, 0x0]), |
| ("{} UTF-32BE", [0x0, 0x0, 0x0, 0x7B, 0x0, 0x0, 0x0, 0x7D]), |
| ("{} UTF-32LE", [0x7B, 0x0, 0x0, 0x0, 0x7D, 0x0, 0x0, 0x0]), |
| |
| // // Single Characters |
| // ("'3' UTF-8", [0x33]), |
| // ("'3' UTF-16BE", [0x0, 0x33]), |
| // ("'3' UTF-16LE", [0x33, 0x0]), |
| ] |
| |
| for (description, encoded) in subjects { |
| let result = try? NSJSONSerialization.JSONObjectWithData(NSData(bytes:UnsafePointer<Void>(encoded), length: encoded.count), options: []) |
| XCTAssertNotNil(result, description) |
| } |
| } |
| |
| } |
| |
| //MARK: - JSONDeserialization |
| extension TestNSJSONSerialization { |
| |
| class var deserializationTests: [(String, TestNSJSONSerialization -> () throws -> Void)] { |
| return [ |
| ("test_deserialize_emptyObject", test_deserialize_emptyObject), |
| ("test_deserialize_multiStringObject", test_deserialize_multiStringObject), |
| |
| ("test_deserialize_emptyArray", test_deserialize_emptyArray), |
| ("test_deserialize_multiStringArray", test_deserialize_multiStringArray), |
| ("test_deserialize_unicodeString", test_deserialize_unicodeString), |
| |
| |
| ("test_deserialize_values", test_deserialize_values), |
| ("test_deserialize_numbers", test_deserialize_numbers), |
| |
| ("test_deserialize_simpleEscapeSequences", test_deserialize_simpleEscapeSequences), |
| ("test_deserialize_unicodeEscapeSequence", test_deserialize_unicodeEscapeSequence), |
| ("test_deserialize_unicodeSurrogatePairEscapeSequence", test_deserialize_unicodeSurrogatePairEscapeSequence), |
| // Disabled due to uninitialized memory SR-606 |
| // ("test_deserialize_allowFragments", test_deserialize_allowFragments), |
| |
| ("test_deserialize_unterminatedObjectString", test_deserialize_unterminatedObjectString), |
| ("test_deserialize_missingObjectKey", test_deserialize_missingObjectKey), |
| ("test_deserialize_unexpectedEndOfFile", test_deserialize_unexpectedEndOfFile), |
| ("test_deserialize_invalidValueInObject", test_deserialize_invalidValueInObject), |
| ("test_deserialize_invalidValueIncorrectSeparatorInObject", test_deserialize_invalidValueIncorrectSeparatorInObject), |
| ("test_deserialize_invalidValueInArray", test_deserialize_invalidValueInArray), |
| ("test_deserialize_badlyFormedArray", test_deserialize_badlyFormedArray), |
| ("test_deserialize_invalidEscapeSequence", test_deserialize_invalidEscapeSequence), |
| ("test_deserialize_unicodeMissingTrailingSurrogate", test_deserialize_unicodeMissingTrailingSurrogate), |
| ] |
| } |
| |
| //MARK: - Object Deserialization |
| func test_deserialize_emptyObject() { |
| let subject = "{}" |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let t = try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| let result = t as? [String: Any] |
| XCTAssertEqual(result?.count, 0) |
| } catch { |
| XCTFail("Error thrown: \(error)") |
| } |
| } |
| |
| func test_deserialize_multiStringObject() { |
| let subject = "{ \"hello\": \"world\", \"swift\": \"rocks\" }" |
| do { |
| for encoding in [NSUTF8StringEncoding, NSUTF16BigEndianStringEncoding] { |
| guard let data = subject.bridge().dataUsingEncoding(encoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String: Any] |
| XCTAssertEqual(result?["hello"] as? String, "world") |
| XCTAssertEqual(result?["swift"] as? String, "rocks") |
| } |
| } catch { |
| XCTFail("Error thrown: \(error)") |
| } |
| } |
| |
| //MARK: - Array Deserialization |
| func test_deserialize_emptyArray() { |
| let subject = "[]" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?.count, 0) |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| func test_deserialize_multiStringArray() { |
| let subject = "[\"hello\", \"swift⚡️\"]" |
| |
| do { |
| for encoding in [NSUTF8StringEncoding, NSUTF16BigEndianStringEncoding] { |
| guard let data = subject.bridge().dataUsingEncoding(encoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?[0] as? String, "hello") |
| XCTAssertEqual(result?[1] as? String, "swift⚡️") |
| } |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| func test_deserialize_unicodeString() { |
| /// Ģ has the same LSB as quotation mark " (U+0022) so test guarding against this case |
| let subject = "[\"unicode\", \"Ģ\", \"😢\"]" |
| |
| do { |
| for encoding in [NSUTF16LittleEndianStringEncoding, NSUTF16BigEndianStringEncoding, NSUTF32LittleEndianStringEncoding, NSUTF32BigEndianStringEncoding] { |
| guard let data = subject.bridge().dataUsingEncoding(encoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?[0] as? String, "unicode") |
| XCTAssertEqual(result?[1] as? String, "Ģ") |
| XCTAssertEqual(result?[2] as? String, "😢") |
| } |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| //MARK: - Value parsing |
| func test_deserialize_values() { |
| let subject = "[true, false, \"hello\", null, {}, []]" |
| |
| do { |
| for encoding in supportedEncodings { |
| guard let data = subject.bridge().dataUsingEncoding(encoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?[0] as? Bool, true) |
| XCTAssertEqual(result?[1] as? Bool, false) |
| XCTAssertEqual(result?[2] as? String, "hello") |
| XCTAssertNotNil(result?[3] as? NSNull) |
| XCTAssertNotNil(result?[4] as? [String:Any]) |
| XCTAssertNotNil(result?[5] as? [Any]) |
| } |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| //MARK: - Number parsing |
| func test_deserialize_numbers() { |
| let subject = "[1, -1, 1.3, -1.3, 1e3, 1E-3]" |
| |
| do { |
| for encoding in supportedEncodings { |
| guard let data = subject.bridge().dataUsingEncoding(encoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?[0] as? Double, 1) |
| XCTAssertEqual(result?[1] as? Double, -1) |
| XCTAssertEqual(result?[2] as? Double, 1.3) |
| XCTAssertEqual(result?[3] as? Double, -1.3) |
| XCTAssertEqual(result?[4] as? Double, 1000) |
| XCTAssertEqual(result?[5] as? Double, 0.001) |
| } |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| //MARK: - Escape Sequences |
| func test_deserialize_simpleEscapeSequences() { |
| let subject = "[\"\\\"\", \"\\\\\", \"\\/\", \"\\b\", \"\\f\", \"\\n\", \"\\r\", \"\\t\"]" |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let res = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| let result = res?.flatMap { $0 as? String } |
| XCTAssertEqual(result?[0], "\"") |
| XCTAssertEqual(result?[1], "\\") |
| XCTAssertEqual(result?[2], "/") |
| XCTAssertEqual(result?[3], "\u{08}") |
| XCTAssertEqual(result?[4], "\u{0C}") |
| XCTAssertEqual(result?[5], "\u{0A}") |
| XCTAssertEqual(result?[6], "\u{0D}") |
| XCTAssertEqual(result?[7], "\u{09}") |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| func test_deserialize_unicodeEscapeSequence() { |
| let subject = "[\"\\u2728\"]" |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?[0] as? String, "✨") |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| func test_deserialize_unicodeSurrogatePairEscapeSequence() { |
| let subject = "[\"\\uD834\\udd1E\"]" |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [Any] |
| XCTAssertEqual(result?[0] as? String, "\u{1D11E}") |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| func test_deserialize_allowFragments() { |
| let subject = "3" |
| |
| do { |
| for encoding in supportedEncodings { |
| guard let data = subject.bridge().dataUsingEncoding(encoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| let result = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments) as? Double |
| XCTAssertEqual(result, 3) |
| } |
| } catch { |
| XCTFail("Unexpected error: \(error)") |
| } |
| } |
| |
| //MARK: - Parsing Errors |
| func test_deserialize_unterminatedObjectString() { |
| let subject = "{\"}" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: UnterminatedString") |
| } catch { |
| // Passing case; the object as unterminated |
| } |
| } |
| |
| func test_deserialize_missingObjectKey() { |
| let subject = "{3}" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Missing key for value") |
| } catch { |
| // Passing case; the key was missing for a value |
| } |
| } |
| |
| func test_deserialize_unexpectedEndOfFile() { |
| let subject = "{" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Unexpected end of file") |
| } catch { |
| // Success |
| } |
| } |
| |
| func test_deserialize_invalidValueInObject() { |
| let subject = "{\"error\":}" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Invalid value") |
| } catch { |
| // Passing case; the value is invalid |
| } |
| } |
| |
| func test_deserialize_invalidValueIncorrectSeparatorInObject() { |
| let subject = "{\"missing\";}" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Invalid value") |
| } catch { |
| // passing case the value is invalid |
| } |
| } |
| |
| func test_deserialize_invalidValueInArray() { |
| let subject = "[," |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Invalid value") |
| } catch { |
| // Passing case; the element in the array is missing |
| } |
| } |
| |
| func test_deserialize_badlyFormedArray() { |
| let subject = "[2b4]" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Badly formed array") |
| } catch { |
| // Passing case; the array is malformed |
| } |
| } |
| |
| func test_deserialize_invalidEscapeSequence() { |
| let subject = "[\"\\e\"]" |
| |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) |
| XCTFail("Expected error: Invalid escape sequence") |
| } catch { |
| // Passing case; the escape sequence is invalid |
| } |
| } |
| |
| func test_deserialize_unicodeMissingTrailingSurrogate() { |
| let subject = "[\"\\uD834\"]" |
| do { |
| guard let data = subject.bridge().dataUsingEncoding(NSUTF8StringEncoding) else { |
| XCTFail("Unable to convert string to data") |
| return |
| } |
| try NSJSONSerialization.JSONObjectWithData(data, options: []) as? [String] |
| XCTFail("Expected error: Missing Trailing Surrogate") |
| } catch { |
| // Passing case; the unicode character is malformed |
| } |
| } |
| |
| } |
| |
| // MARK: - isValidJSONObjectTests |
| extension TestNSJSONSerialization { |
| |
| class var isValidJSONObjectTests: [(String, TestNSJSONSerialization -> () throws -> Void)] { |
| return [ |
| ("test_isValidJSONObjectTrue", test_isValidJSONObjectTrue), |
| ("test_isValidJSONObjectFalse", test_isValidJSONObjectFalse), |
| ] |
| } |
| |
| func test_isValidJSONObjectTrue() { |
| let trueJSON: [Any] = [ |
| // [] |
| Array<Any>(), |
| |
| // [1, ["string", [[]]]] |
| Array<Any>(arrayLiteral: |
| NSNumber(int: 1), |
| Array<Any>(arrayLiteral: |
| "string", |
| Array<Any>(arrayLiteral: |
| Array<Any>() |
| ) |
| ) |
| ), |
| |
| // [NSNull(), ["1" : ["string", 1], "2" : NSNull()]] |
| Array<Any>(arrayLiteral: |
| NSNull(), |
| Dictionary<String, Any>(dictionaryLiteral: |
| ( |
| "1", |
| Array<Any>(arrayLiteral: |
| "string", |
| NSNumber(int: 1) |
| ) |
| ), |
| ( |
| "2", |
| NSNull() |
| ) |
| ) |
| ), |
| |
| // ["0" : 0] |
| Dictionary<String, Any>(dictionaryLiteral: |
| ( |
| "0", |
| NSNumber(int: 0) |
| ) |
| ) |
| ] |
| for testCase in trueJSON { |
| XCTAssertTrue(NSJSONSerialization.isValidJSONObject(testCase)) |
| } |
| } |
| |
| func test_isValidJSONObjectFalse() { |
| let falseJSON: [Any] = [ |
| // 0 |
| NSNumber(int: 0), |
| |
| // NSNull() |
| NSNull(), |
| |
| // "string" |
| "string", |
| |
| // [1, 2, 3, [4 : 5]] |
| Array<Any>(arrayLiteral: |
| NSNumber(int: 1), |
| NSNumber(int: 2), |
| NSNumber(int: 3), |
| Dictionary<NSNumber, Any>(dictionaryLiteral: |
| ( |
| NSNumber(int: 4), |
| NSNumber(int: 5) |
| ) |
| ) |
| ), |
| |
| // [1, 2, Infinity] |
| [NSNumber(int: 1), NSNumber(int: 2), NSNumber(double: 1 / 0)], |
| |
| // [NSNull() : 1] |
| [NSNull() : NSNumber(int: 1)], |
| |
| // [[[[1 : 2]]]] |
| Array<Any>(arrayLiteral: |
| Array<Any>(arrayLiteral: |
| Array<Any>(arrayLiteral: |
| Dictionary<NSNumber, Any>(dictionaryLiteral: |
| ( |
| NSNumber(int: 1), |
| NSNumber(int: 2) |
| ) |
| ) |
| ) |
| ) |
| ) |
| ] |
| for testCase in falseJSON { |
| XCTAssertFalse(NSJSONSerialization.isValidJSONObject(testCase)) |
| } |
| } |
| |
| } |
| |
| // MARK: - serializationTests |
| extension TestNSJSONSerialization { |
| |
| class var serializationTests: [(String, TestNSJSONSerialization -> () throws -> Void)] { |
| return [ |
| ("test_serialize_emptyObject", test_serialize_emptyObject), |
| ("test_serialize_null", test_serialize_null), |
| ("test_serialize_complexObject", test_serialize_complexObject), |
| ("test_nested_array", test_nested_array), |
| ("test_nested_dictionary", test_nested_dictionary), |
| ("test_serialize_number", test_serialize_number), |
| ("test_serialize_stringEscaping", test_serialize_stringEscaping), |
| ("test_serialize_invalid_json", test_serialize_invalid_json), |
| ] |
| } |
| |
| func trySerialize(_ obj: AnyObject) throws -> String { |
| let data = try NSJSONSerialization.dataWithJSONObject(obj, options: []) |
| guard let string = NSString(data: data, encoding: NSUTF8StringEncoding) else { |
| XCTFail("Unable to create string") |
| return "" |
| } |
| return string.bridge() |
| } |
| |
| func test_serialize_emptyObject() { |
| let dict1 = [String: Any]().bridge() |
| XCTAssertEqual(try trySerialize(dict1), "{}") |
| |
| let dict2 = [String: NSNumber]().bridge() |
| XCTAssertEqual(try trySerialize(dict2), "{}") |
| |
| let dict3 = [String: String]().bridge() |
| XCTAssertEqual(try trySerialize(dict3), "{}") |
| |
| let array1 = [String]().bridge() |
| XCTAssertEqual(try trySerialize(array1), "[]") |
| |
| let array2 = [NSNumber]().bridge() |
| XCTAssertEqual(try trySerialize(array2), "[]") |
| } |
| |
| func test_serialize_null() { |
| let arr = [NSNull()].bridge() |
| XCTAssertEqual(try trySerialize(arr), "[null]") |
| |
| let dict = ["a":NSNull()].bridge() |
| XCTAssertEqual(try trySerialize(dict), "{\"a\":null}") |
| |
| let arr2 = [NSNull(), NSNull(), NSNull()].bridge() |
| XCTAssertEqual(try trySerialize(arr2), "[null,null,null]") |
| |
| let dict2 = [["a":NSNull()], ["b":NSNull()], ["c":NSNull()]].bridge() |
| XCTAssertEqual(try trySerialize(dict2), "[{\"a\":null},{\"b\":null},{\"c\":null}]") |
| } |
| |
| func test_serialize_complexObject() { |
| let jsonDict = ["a": 4].bridge() |
| XCTAssertEqual(try trySerialize(jsonDict), "{\"a\":4}") |
| |
| let jsonArr = [1, 2, 3, 4].bridge() |
| XCTAssertEqual(try trySerialize(jsonArr), "[1,2,3,4]") |
| |
| let jsonDict2 = ["a": [1,2]].bridge() |
| XCTAssertEqual(try trySerialize(jsonDict2), "{\"a\":[1,2]}") |
| |
| let jsonArr2 = ["a", "b", "c"].bridge() |
| XCTAssertEqual(try trySerialize(jsonArr2), "[\"a\",\"b\",\"c\"]") |
| |
| let jsonArr3 = [["a":1],["b":2]].bridge() |
| XCTAssertEqual(try trySerialize(jsonArr3), "[{\"a\":1},{\"b\":2}]") |
| |
| let jsonArr4 = [["a":NSNull()],["b":NSNull()]].bridge() |
| XCTAssertEqual(try trySerialize(jsonArr4), "[{\"a\":null},{\"b\":null}]") |
| } |
| |
| func test_nested_array() { |
| var arr = ["a"].bridge() |
| XCTAssertEqual(try trySerialize(arr), "[\"a\"]") |
| |
| arr = [["b"]].bridge() |
| XCTAssertEqual(try trySerialize(arr), "[[\"b\"]]") |
| |
| arr = [[["c"]]].bridge() |
| XCTAssertEqual(try trySerialize(arr), "[[[\"c\"]]]") |
| |
| arr = [[[["d"]]]].bridge() |
| XCTAssertEqual(try trySerialize(arr), "[[[[\"d\"]]]]") |
| } |
| |
| func test_nested_dictionary() { |
| var dict = ["a":1].bridge() |
| XCTAssertEqual(try trySerialize(dict), "{\"a\":1}") |
| |
| dict = ["a":["b":1]].bridge() |
| XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":1}}") |
| |
| dict = ["a":["b":["c":1]]].bridge() |
| XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":{\"c\":1}}}") |
| |
| dict = ["a":["b":["c":["d":1]]]].bridge() |
| XCTAssertEqual(try trySerialize(dict), "{\"a\":{\"b\":{\"c\":{\"d\":1}}}}") |
| } |
| |
| func test_serialize_number() { |
| var json = [1, 1.1, 0, -2].bridge() |
| XCTAssertEqual(try trySerialize(json), "[1,1.1,0,-2]") |
| |
| // Cannot generate "true"/"false" currently |
| json = [NSNumber(bool:false),NSNumber(bool:true)].bridge() |
| XCTAssertEqual(try trySerialize(json), "[0,1]") |
| } |
| |
| func test_serialize_stringEscaping() { |
| var json = ["foo"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"foo\"]") |
| |
| json = ["a\0"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"a\\u0000\"]") |
| |
| json = ["b\\"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"b\\\\\"]") |
| |
| json = ["c\t"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"c\\t\"]") |
| |
| json = ["d\n"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"d\\n\"]") |
| |
| json = ["e\r"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"e\\r\"]") |
| |
| json = ["f\""].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"f\\\"\"]") |
| |
| json = ["g\'"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"g\'\"]") |
| |
| json = ["h\u{7}"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"h\\u0007\"]") |
| |
| json = ["i\u{1f}"].bridge() |
| XCTAssertEqual(try trySerialize(json), "[\"i\\u001f\"]") |
| } |
| |
| func test_serialize_invalid_json() { |
| let str = "Invalid JSON".bridge() |
| do { |
| let _ = try trySerialize(str) |
| XCTFail("Top-level JSON object cannot be string") |
| } catch { |
| // should get here |
| } |
| |
| let double = NSNumber(double: 1.2) |
| do { |
| let _ = try trySerialize(double) |
| XCTFail("Top-level JSON object cannot be double") |
| } catch { |
| // should get here |
| } |
| |
| let dict = [NSNumber(double: 1.2):"a"].bridge() |
| do { |
| let _ = try trySerialize(dict) |
| XCTFail("Dictionary keys must be strings") |
| } catch { |
| // should get here |
| } |
| } |
| } |