diff --git a/ObjectMapper.xcodeproj/project.pbxproj b/ObjectMapper.xcodeproj/project.pbxproj index 7f8e8eae..6356c0e0 100644 --- a/ObjectMapper.xcodeproj/project.pbxproj +++ b/ObjectMapper.xcodeproj/project.pbxproj @@ -386,8 +386,8 @@ isa = PBXGroup; children = ( 6A6AEB971A9387D0002573D3 /* BasicTypes.swift */, - 6A6AEB951A93874F002573D3 /* BasicTypesTestsFromJSON.swift */, 6A3774331A31427F00CC0AB5 /* BasicTypesTestsToJSON.swift */, + 6A6AEB951A93874F002573D3 /* BasicTypesTestsFromJSON.swift */, 6A412A161BAC770C001C3F67 /* ClassClusterTests.swift */, 6A51372E1AADE12C00B82516 /* CustomTransformTests.swift */, C135CAB61D76303E00BA9338 /* DataTransformTests.swift */, diff --git a/Sources/Map.swift b/Sources/Map.swift index 0b434456..a69440ea 100644 --- a/Sources/Map.swift +++ b/Sources/Map.swift @@ -61,6 +61,7 @@ public final class Map { // save key and value associated to it return self[key, delimiter: ".", ignoreNil: false] } + public subscript(key: String, delimiter delimiter: String) -> Map { let nested = key.contains(delimiter) return self[key, nested: nested, delimiter: delimiter, ignoreNil: false] @@ -69,44 +70,49 @@ public final class Map { public subscript(key: String, nested nested: Bool) -> Map { return self[key, nested: nested, delimiter: ".", ignoreNil: false] } + public subscript(key: String, nested nested: Bool, delimiter delimiter: String) -> Map { return self[key, nested: nested, delimiter: delimiter, ignoreNil: false] } - public subscript(key: String, ignoreNil ignoreNil: Bool) -> Map { - return self[key, delimiter: ".", ignoreNil: ignoreNil] - } + public subscript(key: String, ignoreNil ignoreNil: Bool) -> Map { + return self[key, delimiter: ".", ignoreNil: ignoreNil] + } + public subscript(key: String, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { let nested = key.contains(delimiter) return self[key, nested: nested, delimiter: delimiter, ignoreNil: ignoreNil] } - public subscript(key: String, nested nested: Bool, ignoreNil ignoreNil: Bool) -> Map { - return self[key, nested: nested, delimiter: ".", ignoreNil: ignoreNil] - } + public subscript(key: String, nested nested: Bool, ignoreNil ignoreNil: Bool) -> Map { + return self[key, nested: nested, delimiter: ".", ignoreNil: ignoreNil] + } + public subscript(key: String, nested nested: Bool, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { // save key and value associated to it currentKey = key keyIsNested = nested nestedKeyDelimiter = delimiter - // check if a value exists for the current key - // do this pre-check for performance reasons - if nested == false { - let object = JSON[key] - let isNSNull = object is NSNull - isKeyPresent = isNSNull ? true : object != nil - currentValue = isNSNull ? nil : object - } else { - // break down the components of the key that are separated by . - (isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON) + if mappingType == .fromJSON { + // check if a value exists for the current key + // do this pre-check for performance reasons + if nested == false { + let object = JSON[key] + let isNSNull = object is NSNull + isKeyPresent = isNSNull ? true : object != nil + currentValue = isNSNull ? nil : object + } else { + // break down the components of the key that are separated by . + (isKeyPresent, currentValue) = valueFor(ArraySlice(key.components(separatedBy: delimiter)), dictionary: JSON) + } + + // update isKeyPresent if ignoreNil is true + if ignoreNil && currentValue == nil { + isKeyPresent = false + } } - // update isKeyPresent if ignoreNil is true - if ignoreNil && currentValue == nil { - isKeyPresent = false - } - return self } diff --git a/Tests/ObjectMapperTests/PerformanceTests.swift b/Tests/ObjectMapperTests/PerformanceTests.swift index cf65d0bb..c03650ff 100644 --- a/Tests/ObjectMapperTests/PerformanceTests.swift +++ b/Tests/ObjectMapperTests/PerformanceTests.swift @@ -32,6 +32,19 @@ import ObjectMapper class PerformanceTests: XCTestCase { + let JSONTestString: String = { + let subObjectJSON = "{\"string\":\"This is a string\", \"int\": 12,\"double\":12.27,\"float\":12.3212, \"bool\":false, \"arr\":[ \"bla\", true, 42 ], \"dict\":{ \"key1\" : \"value1\", \"key2\" : false, \"key3\" : 142 } }" + + let objectJSONString = "{\"string\":\"This is a string\", \"int\": 12,\"double\":12.27,\"float\":12.3212, \"bool\":false, \"arr\":[ \"bla\", true, 42 ], \"dict\":{ \"key1\" : \"value1\", \"key2\" : false, \"key3\" : 142 }, \"object\": \(subObjectJSON), \"objects\":{ \"key1\": \(subObjectJSON), \"key2\": \(subObjectJSON)}}" + + var JSONString = "[" + for _ in 0...1000 { + JSONString += "\(objectJSONString)," + } + JSONString += "\(objectJSONString)]" + return JSONString + }() + override func setUp() { super.setUp() // Put setup code here. This method is called before the invocation of each test method in the class. @@ -41,133 +54,121 @@ class PerformanceTests: XCTestCase { // Put teardown code here. This method is called after the invocation of each test method in the class. super.tearDown() } - - func createJSONString(_ count: Int = 1000) -> String { - let subPersonJSON = "{\"identifier\" : \"user8723\", \"drinker\" : true, \"age\": 17, \"username\" : \"sub user\" }" - - let personJSONString = "{\"username\":\"John Doe\",\"identifier\":\"identifier\",\"photoCount\":12,\"age\":1227,\"drinker\":true,\"smoker\":false, \"arr\":[ \"bla\", true, 42 ], \"dict\":{ \"key1\" : \"value1\", \"key2\" : false, \"key3\" : 142 }, \"arrOpt\":[ \"bla\", true, 42 ], \"dictOpt\":{ \"key1\" : \"value1\", \"key2\" : false, \"key3\" : 142 }, \"weight\": 122.22, \"float\": 123.331, \"friend\": \(subPersonJSON), \"friendDictionary\":{ \"bestFriend\": \(subPersonJSON)}}" - - var JSONString = "[" - for _ in 0...count { - JSONString += "\(personJSONString)," - } - JSONString += "\(personJSONString)]" - return JSONString - } - + func testPerformance() { - let JSONString = createJSONString() - self.measure { // Put the code you want to measure the time of here. - _ = Mapper().mapArray(JSONString: JSONString) + _ = Mapper().mapArray(JSONString: self.JSONTestString) } } func testPerformanceCluster() { - let JSONString = createJSONString() - self.measure { // Put the code you want to measure the time of here. - _ = Mapper().mapArray(JSONString: JSONString) + _ = Mapper().mapArray(JSONString: self.JSONTestString) + } + } + + func testPerformanceImmutable() { + self.measure { + _ = try? Mapper().mapArray(JSONString: self.JSONTestString) } } } -class Person: Mappable { +class PerformanceMappableObject: Mappable { - var username: String = "" - var identifier: String? - var photoCount: Int = 0 - var age: Int? - var weight: Double? + var string: String? + var int: Int? + var double: Double? var float: Float? - var drinker: Bool = false - var smoker: Bool? - var arr: [AnyObject] = [] - var arrOptional: [AnyObject]? - var dict: [String: AnyObject] = [:] - var dictKey1: String? - var dictOptional: [String: AnyObject]? - var dictString: [String: String]? - var friendDictionary: [String: Person]? - var friend: Person? - var friends: [Person]? = [] - - init(){ - - } + var bool: Bool? + var array: [Any]? + var dictionary: [String: Any]? + var object: PerformanceMappableObject? + var objects: [PerformanceMappableObject]? required init?(map: Map){ } func mapping(map: Map) { - username <- map["username"] - identifier <- map["identifier"] - photoCount <- map["photoCount"] - age <- map["age"] - weight <- map["weight"] - float <- map["float"] - drinker <- map["drinker"] - smoker <- map["smoker"] - arr <- map["arr"] - arrOptional <- map["arrOpt"] - dict <- map["dict"] - dictKey1 <- map["dict.key1"] - dictOptional <- map["dictOpt"] - friend <- map["friend"] - friends <- map["friends"] - friendDictionary <- map["friendDictionary"] - dictString <- map["dictString"] + string <- map["string"] + int <- map["int"] + double <- map["double"] + float <- map["float"] + bool <- map["bool"] + array <- map["array"] + dictionary <- map["dictionary"] + object <- map["object"] + objects <- map["objects"] } } -class PersonCluster: StaticMappable { +class PerformanceStaticMappableObject: StaticMappable { - var username: String = "" - var identifier: String? - var photoCount: Int = 0 - var age: Int? - var weight: Double? + var string: String? + var int: Int? + var double: Double? var float: Float? - var drinker: Bool = false - var smoker: Bool? - var arr: [AnyObject] = [] - var arrOptional: [AnyObject]? - var dict: [String : AnyObject] = [:] - var dictKey1: String? - var dictOptional: [String: AnyObject]? - var dictString: [String: String]? - var friendDictionary: [String: Person]? - var friend: Person? - var friends: [Person]? = [] + var bool: Bool? + var array: [Any]? + var dictionary: [String: Any]? + var object: PerformanceStaticMappableObject? + var objects: [PerformanceStaticMappableObject]? - init(){ - + static func objectForMapping(map: Map) -> BaseMappable? { + return PerformanceStaticMappableObject() } - static func objectForMapping(map: Map) -> BaseMappable? { - return PersonCluster() + func mapping(map: Map) { + string <- map["string"] + int <- map["int"] + double <- map["double"] + float <- map["float"] + bool <- map["bool"] + array <- map["array"] + dictionary <- map["dictionary"] + object <- map["object"] + objects <- map["objects"] + } +} + +class PerformanceImmutableMappableObject: ImmutableMappable { + + let string: String? + let int: Int? + let double: Double? + let float: Float? + let bool: Bool? + let array: [Any]? + let dictionary: [String: Any]? + let object: PerformanceImmutableMappableObject? + let objects: [PerformanceImmutableMappableObject]? + + required init(map: Map) throws { + string = try map.value("string") + int = try map.value("int") + double = try map.value("double") + float = try map.value("float") + bool = try map.value("bool") + array = try map.value("array") + dictionary = try map.value("dictionary") + object = try map.value("object") + objects = try map.value("objects") } func mapping(map: Map) { - username <- map["username"] - identifier <- map["identifier"] - photoCount <- map["photoCount"] - age <- map["age"] - weight <- map["weight"] - float <- map["float"] - drinker <- map["drinker"] - smoker <- map["smoker"] - arr <- map["arr"] - arrOptional <- map["arrOpt"] - dict <- map["dict"] - dictKey1 <- map["dict.key1"] - dictOptional <- map["dictOpt"] - friend <- map["friend"] - friends <- map["friends"] - friendDictionary <- map["friendDictionary"] - dictString <- map["dictString"] + string >>> map["string"] + int >>> map["int"] + double >>> map["double"] + float >>> map["float"] + bool >>> map["bool"] + array >>> map["array"] + dictionary >>> map["dictionary"] + object >>> map["object"] + objects >>> map["objects"] } } + +