A json array that contains null always fails to be mapped to a swift array that has optional elements.
public var result: [Product?]!
required public init?(_ map: Map) {
result <- map["result"] // `result` will always be nil
}
ObjectMapper doesn't actually support arrays of optionals. When mapping arrays, it only adds objects that have been successfully mapped.
Hi,
I am really stuck on this limitation with a [Int?]? attribute which I get from a web service.
Is there any workaround you could suggest me to deal with it: maybe a custom transform (which would transform nil values into 0) could work?
Thanks.
Ok, I have a workaround if someone has the same problem, I guess it should also work with a custom objects array.
import ObjectMapper
class MyDto: Mappable {
var dangerLevels: [Int]? // in json, it is in fact a [Int?]? representation
required init?(_ map: Map) {
let levels = map["dangers_levels"].value() as [AnyObject]?
if let levels = levels {
dangerLevels = []
for level in levels {
if level is NSNull {
dangerLevels!.append(0)
} else {
dangerLevels!.append(level as! Int)
}
}
}
}
func mapping(map: Map) {
//dangerLevels <- map["dangers_levels"]
}
}
Here is a swipe at a generic version that is working for us.
final class MapHelper<T> {
let map: Map
init(map: Map) {
self.map = map
}
func mapArrayOfOptionals(field: String) -> [T?] {
if let values = map[field].value() as [AnyObject]? {
var resultValues = [T?]()
for value in values {
if value is NSNull {
resultValues.append(nil)
} else {
resultValues.append(value as? T)
}
}
return resultValues
}
return []
}
}
struct SomeDTO: Mappable {
private(set) var optionalStrings: [String?]!
init?(_ map: Map) {}
mutating func mapping(map: Map) {
optionalStrings = MapHelper<String>(map: map).mapArrayOfOptionals("items")
}
}
Is something along these lines viable as a PR for the project. Happy to take suggestions and take a stab at it if desired.
Awesome, thanks!!
Thanks for the solution! Kinda get caught off guard when knowing the following JSON validates.
[
{
"name": "a",
"type": 1
},
null,
{
"name": "b",
"type": 2
}
]
@KevM Having a hard time implementing the generic solution for my custom object. If my custom struct has nested custom structs that rely on Object Mapper to parse it, is this solution sill expected to work?
To add onto @KevM's solution above. This implementation allows nested mapping if your optional value is Mappable.
Same usage as the solution by @KevM above.
final class MapHelper<T:Mappable> {
let map: Map
init(map: Map) {
self.map = map
}
func flatMapArrayOfOptionals(field: String) -> [T] {
if let values = map[field].value() as [AnyObject]? {
var resultValues = [T]()
for value in values {
if value is NSNull {
// do nothing
} else {
if let mappableValue = Mapper<T>().map(value) {
resultValues.append(mappableValue)
} else if let nonMappableValue = value as? T {
resultValues.append(nonMappableValue)
}
}
}
return resultValues
}
return []
}
func mapArrayOfOptionals(field: String) -> [T?] {
if let values = map[field].value() as [AnyObject]? {
var resultValues = [T?]()
for value in values {
if value is NSNull {
resultValues.append(nil)
} else {
if let mappableValue = Mapper<T>().map(value) {
resultValues.append(mappableValue)
} else {
resultValues.append(value as? T)
}
}
}
return resultValues
}
return []
}
}
Guys I have the following object:
struct Repo: ImmutableMappable {
let name: String
let description: String?
let author: String
let profilePicture: URL
let stars: Int
let forks: Int
let fullName: String
// MARK: Non-argument initializer
init() {
name = ""
description = nil
author = ""
profilePicture = EnvironmentVariable.profileURL.url
stars = 0
forks = 0
fullName = ""
}
// MARK: ImmutableMappable conforms
init(map: Map) throws {
name = try map.value("name")
description = try map.value("description")
author = try map.value("owner.login")
profilePicture = try map.value("owner.avatar_url", using: URLTransform())
stars = try map.value("stargazers_count")
forks = try map.value("forks_count")
fullName = try map.value("full_name")
}
}
And it's failing when description property is null, how can I handle it?
/// Returns a value or throws an error.
public func value<T>(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> T {
let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter)
guard let value = currentValue as? T else {
throw MapError(key: key, currentValue: currentValue, reason: "Cannot cast to '\(T.self)'", file: file, function: function, line: line)
}
return value
}
Most helpful comment
Guys I have the following object:
And it's failing when
descriptionproperty isnull, how can I handle it?