Blame view
Pods/ObjectMapper/Sources/Map.swift
6.28 KB
d774f0637
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 |
// // Map.swift // ObjectMapper // // Created by Tristan Himmelman on 2015-10-09. // // The MIT License (MIT) // // Copyright (c) 2014-2016 Hearst // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN // THE SOFTWARE. import Foundation /// MapContext is available for developers who wish to pass information around during the mapping process. public protocol MapContext { } /// A class used for holding mapping data public final class Map { public let mappingType: MappingType public internal(set) var JSON: [String: Any] = [:] public internal(set) var isKeyPresent = false public internal(set) var currentValue: Any? public internal(set) var currentKey: String? 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, 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. /// The Key paramater can be a period separated string (ex. "distance.value") to access sub objects. public subscript(key: String) -> 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] } 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, 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, delimiter delimiter: String, ignoreNil ignoreNil: Bool) -> Map { // save key and value associated to it currentKey = key keyIsNested = nested nestedKeyDelimiter = delimiter 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 } } return self } public func value<T>() -> T? { return currentValue as? T } } /// Fetch value from JSON dictionary, loop through keyPathComponents until we reach the desired object private func valueFor(_ keyPathComponents: ArraySlice<String>, dictionary: [String: Any]) -> (Bool, Any?) { // Implement it as a tail recursive function. if keyPathComponents.isEmpty { return (false, nil) } if let keyPath = keyPathComponents.first { let object = dictionary[keyPath] if object is NSNull { return (true, nil) } else if keyPathComponents.count > 1, let dict = object as? [String: Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, dictionary: dict) } else if keyPathComponents.count > 1, let array = object as? [Any] { let tail = keyPathComponents.dropFirst() return valueFor(tail, array: array) } else { return (object != nil, object) } } return (false, nil) } /// Fetch value from JSON Array, loop through keyPathComponents them until we reach the desired object private func valueFor(_ keyPathComponents: ArraySlice<String>, array: [Any]) -> (Bool, Any?) { // Implement it as a tail recursive function. if keyPathComponents.isEmpty { return (false, nil) } //Try to convert keypath to Int as index 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 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) } |