Skip to content

Commit

Permalink
Merge pull request #697 from Hearst-DD/ConnectCorp-feature/allow-nil-…
Browse files Browse the repository at this point in the history
…to-json

ToJSON mappings can now print nil values if shouldIncludeNilValues is set to true
  • Loading branch information
tristanhimmelman committed Dec 11, 2016
2 parents de25cc2 + 45b3b51 commit fc44253
Show file tree
Hide file tree
Showing 6 changed files with 48 additions and 28 deletions.
2 changes: 1 addition & 1 deletion ObjectMapper.podspec
@@ -1,6 +1,6 @@
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'
Expand Down
2 changes: 1 addition & 1 deletion Sources/ImmutableMappable.swift
Expand Up @@ -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,
Expand Down
35 changes: 19 additions & 16 deletions Sources/Map.swift
Expand Up @@ -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 /// 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

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.
Expand Down Expand Up @@ -133,10 +136,10 @@ private func valueFor(_ keyPathComponents: ArraySlice<String>, 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 {
Expand All @@ -159,19 +162,19 @@ private func valueFor(_ keyPathComponents: ArraySlice<String>, 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)
Expand Down
10 changes: 6 additions & 4 deletions Sources/Mapper.swift
Expand Up @@ -37,9 +37,11 @@ public enum MappingType {
public final class Mapper<N: BaseMappable> {

public var context: MapContext?
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){
public init(context: MapContext? = nil, shouldIncludeNilValues: Bool = false){
self.context = context
self.shouldIncludeNilValues = shouldIncludeNilValues
}

// MARK: Mapping functions that map to an existing object toObject
Expand All @@ -65,7 +67,7 @@ public final class Mapper<N: BaseMappable> {
/// 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
}
Expand All @@ -92,7 +94,7 @@ public final class Mapper<N: BaseMappable> {

/// 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 {
Expand Down Expand Up @@ -272,7 +274,7 @@ extension Mapper {
///Maps an object that conforms to Mappable to a JSON dictionary <String, Any>
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
}
Expand Down
15 changes: 9 additions & 6 deletions Sources/ToJSON.swift
Expand Up @@ -73,6 +73,7 @@ internal final class ToJSON {
|| x is Double
|| x is Float
|| x is String
|| x is NSNull
|| x is Array<NSNumber> // Arrays
|| x is Array<Bool>
|| x is Array<Int>
Expand All @@ -92,10 +93,12 @@ internal final class ToJSON {
setValue(x, map: map)
}
}

class func optionalBasicType<N>(_ field: N?, map: Map) {
if let field = field {
basicType(field, map: map)
} else if map.shouldIncludeNilValues {
basicType(NSNull(), map: map) //If BasicType is nil, emil NSNull into the JSON output
}
}

Expand Down Expand Up @@ -155,13 +158,13 @@ internal final class ToJSON {

setValue(JSONObjects, map: map)
}

class func optionalObjectDictionary<N: BaseMappable>(_ field: Dictionary<String, N>?, map: Map) {
if let field = field {
if let field = field {
objectDictionary(field, map: map)
}
}
}
}

class func objectDictionaryOfArrays<N: BaseMappable>(_ field: Dictionary<String, [N]>, map: Map) {
let JSONObjects = Mapper(context: map.context).toJSONDictionaryOfArrays(field)

Expand Down
12 changes: 12 additions & 0 deletions Tests/ObjectMapperTests/BasicTypesTestsToJSON.swift
Expand Up @@ -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<BasicTypes>(shouldIncludeNilValues: true).toJSONString(object, prettyPrint: true)
let JSONWithoutNil = Mapper<BasicTypes>(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()
Expand Down

0 comments on commit fc44253

Please sign in to comment.