From 3f87871ec708199b2128ab809b8a794a6d39402f Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 5 Dec 2016 18:32:54 -0800 Subject: [PATCH 1/6] emit Null into JSON if field is nil --- Sources/ToJSON.swift | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/Sources/ToJSON.swift b/Sources/ToJSON.swift index 4d595325..fcc0cf5d 100644 --- a/Sources/ToJSON.swift +++ b/Sources/ToJSON.swift @@ -45,9 +45,9 @@ private func setValue(_ value: Any, forKeyPathComponents components: ArraySlice< if components.isEmpty { return } - + let head = components.first! - + if components.count == 1 { dictionary[String(head)] = value } else { @@ -55,10 +55,10 @@ private func setValue(_ value: Any, forKeyPathComponents components: ArraySlice< if child == nil { child = [:] } - + let tail = components.dropFirst() setValue(value, forKeyPathComponents: tail, dictionary: &child!) - + dictionary[String(head)] = child } } @@ -73,6 +73,7 @@ internal final class ToJSON { || x is Double || x is Float || x is String + || x is NSNull || x is Array // Arrays || x is Array || x is Array @@ -92,13 +93,15 @@ internal final class ToJSON { setValue(x, map: map) } } - + class func optionalBasicType(_ field: N?, map: Map) { if let field = field { basicType(field, map: map) + } else { + basicType(NSNull(), map: map) //If BasicType is nil, emil NSNull into the JSON output } } - + class func object(_ field: N, map: Map) { if let result = Mapper(context: map.context).toJSON(field) as Any? { setValue(result, map: map) @@ -110,7 +113,7 @@ internal final class ToJSON { object(field, map: map) } } - + class func objectArray(_ field: Array, map: Map) { let JSONObjects = Mapper(context: map.context).toJSONArray(field) @@ -157,14 +160,14 @@ internal final class ToJSON { } class func optionalObjectDictionary(_ field: Dictionary?, map: Map) { - if let field = field { + if let field = field { objectDictionary(field, map: map) - } - } + } + } class func objectDictionaryOfArrays(_ field: Dictionary, map: Map) { let JSONObjects = Mapper(context: map.context).toJSONDictionaryOfArrays(field) - + setValue(JSONObjects, map: map) } From 8645b4711607240e0bb2160d058608127d18e808 Mon Sep 17 00:00:00 2001 From: Tray Lewin Date: Mon, 5 Dec 2016 18:35:37 -0800 Subject: [PATCH 2/6] remove spaces --- Sources/ToJSON.swift | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Sources/ToJSON.swift b/Sources/ToJSON.swift index fcc0cf5d..9d36bcb1 100644 --- a/Sources/ToJSON.swift +++ b/Sources/ToJSON.swift @@ -45,9 +45,9 @@ private func setValue(_ value: Any, forKeyPathComponents components: ArraySlice< if components.isEmpty { return } - + let head = components.first! - + if components.count == 1 { dictionary[String(head)] = value } else { @@ -55,10 +55,10 @@ private func setValue(_ value: Any, forKeyPathComponents components: ArraySlice< if child == nil { child = [:] } - + let tail = components.dropFirst() setValue(value, forKeyPathComponents: tail, dictionary: &child!) - + dictionary[String(head)] = child } } @@ -101,7 +101,7 @@ internal final class ToJSON { basicType(NSNull(), map: map) //If BasicType is nil, emil NSNull into the JSON output } } - + class func object(_ field: N, map: Map) { if let result = Mapper(context: map.context).toJSON(field) as Any? { setValue(result, map: map) @@ -113,7 +113,7 @@ internal final class ToJSON { object(field, map: map) } } - + class func objectArray(_ field: Array, map: Map) { let JSONObjects = Mapper(context: map.context).toJSONArray(field) @@ -158,16 +158,16 @@ internal final class ToJSON { setValue(JSONObjects, map: map) } - + class func optionalObjectDictionary(_ field: Dictionary?, map: Map) { if let field = field { objectDictionary(field, map: map) } } - + class func objectDictionaryOfArrays(_ field: Dictionary, map: Map) { let JSONObjects = Mapper(context: map.context).toJSONDictionaryOfArrays(field) - + setValue(JSONObjects, map: map) } From c93e462ec49d19c445adc5fc6cf14c31ead08e8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fai=C3=A7al=20Tchirou?= Date: Tue, 6 Dec 2016 19:48:09 +0100 Subject: [PATCH 3/6] Update Podspec --- ObjectMapper.podspec | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ObjectMapper.podspec b/ObjectMapper.podspec index abdd01f0..060d3b97 100644 --- a/ObjectMapper.podspec +++ b/ObjectMapper.podspec @@ -1,11 +1,11 @@ Pod::Spec.new do |s| s.name = 'ObjectMapper' - s.version = '2.2.1' + s.version = '2.2.2' s.license = 'MIT' s.summary = 'JSON Object mapping written in Swift' s.homepage = 'https://github.com/Hearst-DD/ObjectMapper' s.authors = { 'Tristan Himmelman' => 'tristanhimmelman@gmail.com' } - s.source = { :git => 'https://github.com/Hearst-DD/ObjectMapper.git', :tag => s.version.to_s } + s.source = { :git => 'git@github.com:ConnectCorp/ObjectMapper.git', :tag => s.version.to_s } s.watchos.deployment_target = '2.0' s.ios.deployment_target = '8.0' From 4b5a2bd0bae97052a7c0caa4d2f4e9f63b962e81 Mon Sep 17 00:00:00 2001 From: Tristan Himmelman Date: Sun, 11 Dec 2016 17:02:14 -0500 Subject: [PATCH 4/6] - added shouldIncludeNilValues as member variable of Mapper and Map - only printing null when property is set to true - added test --- Sources/ImmutableMappable.swift | 2 +- Sources/Map.swift | 35 ++++++++++--------- Sources/Mapper.swift | 10 +++--- Sources/ToJSON.swift | 2 +- .../BasicTypesTestsToJSON.swift | 12 +++++++ 5 files changed, 39 insertions(+), 22 deletions(-) diff --git a/Sources/ImmutableMappable.swift b/Sources/ImmutableMappable.swift index e187d801..23eebdd3 100644 --- a/Sources/ImmutableMappable.swift +++ b/Sources/ImmutableMappable.swift @@ -242,7 +242,7 @@ public extension Mapper where N: ImmutableMappable { internal extension Mapper where N: BaseMappable { internal func mapOrFail(JSON: [String: Any]) throws -> N { - let map = Map(mappingType: .fromJSON, JSON: JSON, context: context) + let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues) // Check if object is ImmutableMappable, if so use ImmutableMappable protocol for mapping if let klass = N.self as? ImmutableMappable.Type, diff --git a/Sources/Map.swift b/Sources/Map.swift index a69440ea..4d5edd4b 100644 --- a/Sources/Map.swift +++ b/Sources/Map.swift @@ -45,14 +45,17 @@ public final class Map { var keyIsNested = false public internal(set) var nestedKeyDelimiter: String = "." public var context: MapContext? + public var shouldIncludeNilValues = false let toObject: Bool // indicates whether the mapping is being applied to an existing object - public init(mappingType: MappingType, JSON: [String: Any], toObject: Bool = false, context: MapContext? = nil) { + public init(mappingType: MappingType, JSON: [String: Any], toObject: Bool = false, context: MapContext? = nil, shouldIncludeNilValues: Bool = false) { + self.mappingType = mappingType self.JSON = JSON self.toObject = toObject self.context = context + self.shouldIncludeNilValues = shouldIncludeNilValues } /// Sets the current mapper value and key. @@ -133,10 +136,10 @@ private func valueFor(_ keyPathComponents: ArraySlice, dictionary: [Stri let object = dictionary[keyPath] if object is NSNull { return (true, nil) - } else if let dict = object as? [String: Any] , keyPathComponents.count > 1 { + } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, dictionary: dict) - } else if let array = object as? [Any] , keyPathComponents.count > 1 { + } else if keyPathComponents.count > 1, let array = object as? [Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, array: array) } else { @@ -159,19 +162,19 @@ private func valueFor(_ keyPathComponents: ArraySlice, array: [Any]) -> if let keyPath = keyPathComponents.first, let index = Int(keyPath) , index >= 0 && index < array.count { - let object = array[index] - - if object is NSNull { - return (true, nil) - } else if let array = object as? [Any] , keyPathComponents.count > 1 { - let tail = keyPathComponents.dropFirst() - return valueFor(tail, array: array) - } else if let dict = object as? [String: Any] , keyPathComponents.count > 1 { - let tail = keyPathComponents.dropFirst() - return valueFor(tail, dictionary: dict) - } else { - return (true, object) - } + let object = array[index] + + if object is NSNull { + return (true, nil) + } else if keyPathComponents.count > 1, let array = object as? [Any] { + let tail = keyPathComponents.dropFirst() + return valueFor(tail, array: array) + } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { + let tail = keyPathComponents.dropFirst() + return valueFor(tail, dictionary: dict) + } else { + return (true, object) + } } return (false, nil) diff --git a/Sources/Mapper.swift b/Sources/Mapper.swift index d84cc155..5fc90ac3 100755 --- a/Sources/Mapper.swift +++ b/Sources/Mapper.swift @@ -37,9 +37,11 @@ public enum MappingType { public final class Mapper { public var context: MapContext? + public var shouldIncludeNilValues = false - public init(context: MapContext? = nil){ + public init(context: MapContext? = nil, shouldIncludeNilValues: Bool = false){ self.context = context + self.shouldIncludeNilValues = shouldIncludeNilValues } // MARK: Mapping functions that map to an existing object toObject @@ -65,7 +67,7 @@ public final class Mapper { /// Usefull for those pesky objects that have crappy designated initializers like NSManagedObject public func map(JSON: [String: Any], toObject object: N) -> N { var mutableObject = object - let map = Map(mappingType: .fromJSON, JSON: JSON, toObject: true, context: context) + let map = Map(mappingType: .fromJSON, JSON: JSON, toObject: true, context: context, shouldIncludeNilValues: shouldIncludeNilValues) mutableObject.mapping(map: map) return mutableObject } @@ -92,7 +94,7 @@ public final class Mapper { /// Maps a JSON dictionary to an object that conforms to Mappable public func map(JSON: [String: Any]) -> N? { - let map = Map(mappingType: .fromJSON, JSON: JSON, context: context) + let map = Map(mappingType: .fromJSON, JSON: JSON, context: context, shouldIncludeNilValues: shouldIncludeNilValues) if let klass = N.self as? StaticMappable.Type { // Check if object is StaticMappable if var object = klass.objectForMapping(map: map) as? N { @@ -272,7 +274,7 @@ extension Mapper { ///Maps an object that conforms to Mappable to a JSON dictionary public func toJSON(_ object: N) -> [String: Any] { var mutableObject = object - let map = Map(mappingType: .toJSON, JSON: [:], context: context) + let map = Map(mappingType: .toJSON, JSON: [:], context: context, shouldIncludeNilValues: shouldIncludeNilValues) mutableObject.mapping(map: map) return map.JSON } diff --git a/Sources/ToJSON.swift b/Sources/ToJSON.swift index 9d36bcb1..b1eff4b7 100644 --- a/Sources/ToJSON.swift +++ b/Sources/ToJSON.swift @@ -97,7 +97,7 @@ internal final class ToJSON { class func optionalBasicType(_ field: N?, map: Map) { if let field = field { basicType(field, map: map) - } else { + } else if map.shouldIncludeNilValues { basicType(NSNull(), map: map) //If BasicType is nil, emil NSNull into the JSON output } } diff --git a/Tests/ObjectMapperTests/BasicTypesTestsToJSON.swift b/Tests/ObjectMapperTests/BasicTypesTestsToJSON.swift index 703d876f..55ca4954 100644 --- a/Tests/ObjectMapperTests/BasicTypesTestsToJSON.swift +++ b/Tests/ObjectMapperTests/BasicTypesTestsToJSON.swift @@ -46,6 +46,18 @@ class BasicTypesTestsToJSON: XCTestCase { // MARK: Test mapping to JSON and back (basic types: Bool, Int, Double, Float, String) + func testShouldIncludeNilValues(){ + let object = BasicTypes() + + let JSONWithNil = Mapper(shouldIncludeNilValues: true).toJSONString(object, prettyPrint: true) + let JSONWithoutNil = Mapper(shouldIncludeNilValues: false).toJSONString(object, prettyPrint: true) + + //TODO This test could be improved + XCTAssertNotNil(JSONWithNil) + XCTAssertTrue((JSONWithNil!.characters.count) > 5) + XCTAssertTrue((JSONWithNil!.characters.count) != (JSONWithoutNil!.characters.count)) + } + func testMappingBoolToJSON(){ let value: Bool = true let object = BasicTypes() From 03c49e0f3f68668786b77d46230ae6efa5568188 Mon Sep 17 00:00:00 2001 From: Tristan Himmelman Date: Sun, 11 Dec 2016 17:03:20 -0500 Subject: [PATCH 5/6] updated pod spec --- ObjectMapper.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ObjectMapper.podspec b/ObjectMapper.podspec index 060d3b97..fee08a62 100644 --- a/ObjectMapper.podspec +++ b/ObjectMapper.podspec @@ -5,7 +5,7 @@ Pod::Spec.new do |s| s.summary = 'JSON Object mapping written in Swift' s.homepage = 'https://github.com/Hearst-DD/ObjectMapper' s.authors = { 'Tristan Himmelman' => 'tristanhimmelman@gmail.com' } - s.source = { :git => 'git@github.com:ConnectCorp/ObjectMapper.git', :tag => s.version.to_s } + s.source = { :git => 'https://github.com/Hearst-DD/ObjectMapper.git', :tag => s.version.to_s } s.watchos.deployment_target = '2.0' s.ios.deployment_target = '8.0' From 45b3b5197d08a0be26c2b820d38b6088b10d55bf Mon Sep 17 00:00:00 2001 From: Tristan Himmelman Date: Sun, 11 Dec 2016 17:06:26 -0500 Subject: [PATCH 6/6] Added comment --- Sources/Map.swift | 2 +- Sources/Mapper.swift | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Sources/Map.swift b/Sources/Map.swift index 4d5edd4b..22f87b5a 100644 --- a/Sources/Map.swift +++ b/Sources/Map.swift @@ -45,7 +45,7 @@ public final class Map { var keyIsNested = false public internal(set) var nestedKeyDelimiter: String = "." public var context: MapContext? - public var shouldIncludeNilValues = false + public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set. let toObject: Bool // indicates whether the mapping is being applied to an existing object diff --git a/Sources/Mapper.swift b/Sources/Mapper.swift index 5fc90ac3..9071acc0 100755 --- a/Sources/Mapper.swift +++ b/Sources/Mapper.swift @@ -37,7 +37,7 @@ public enum MappingType { public final class Mapper { public var context: MapContext? - public var shouldIncludeNilValues = false + public var shouldIncludeNilValues = false /// If this is set to true, toJSON output will include null values for any variables that are not set. public init(context: MapContext? = nil, shouldIncludeNilValues: Bool = false){ self.context = context