Skip to content

Commit

Permalink
- only evaluating the currentValue in map for fromJSON mappings
Browse files Browse the repository at this point in the history
- improvements to performance tests
  • Loading branch information
tristanhimmelman committed Nov 8, 2016
1 parent ed32589 commit 19477dd
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 118 deletions.
2 changes: 1 addition & 1 deletion ObjectMapper.xcodeproj/project.pbxproj
Expand Up @@ -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 */,
Expand Down
48 changes: 27 additions & 21 deletions Sources/Map.swift
Expand Up @@ -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]
Expand All @@ -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
}

Expand Down
193 changes: 97 additions & 96 deletions Tests/ObjectMapperTests/PerformanceTests.swift
Expand Up @@ -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.
Expand All @@ -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<Person>().mapArray(JSONString: JSONString)
_ = Mapper<PerformanceMappableObject>().mapArray(JSONString: self.JSONTestString)
}
}

func testPerformanceCluster() {
let JSONString = createJSONString()

self.measure {
// Put the code you want to measure the time of here.
_ = Mapper<PersonCluster>().mapArray(JSONString: JSONString)
_ = Mapper<PerformanceStaticMappableObject>().mapArray(JSONString: self.JSONTestString)
}
}

func testPerformanceImmutable() {
self.measure {
_ = try? Mapper<PerformanceImmutableMappableObject>().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"]
}
}


0 comments on commit 19477dd

Please sign in to comment.