I have not been able to figure out how to map this JSON
{
"market_estimates": {
"2000": {
"0": 12830,
"10000": 11041
},
"2001": {
"0": 12830,
"10000": 11041
}
}
}
Into an object like this:
class MarketEstimate {
var year: Int = 0
var price: Int = 0
var mileage: Int = 0
}
How do I perform this task?
@mikkelam, do you want an array of MarketEstimate?
Yes, I am sorry i did not specify. Do you know how I could solve this?
Is this what you want? I have no idea where price and mileage come from.
Input
{
"market_estimates": {
"2000": {
"0": 12830,
"10000": 11041
},
"2001": {
"0": 12830,
"10000": 11041
}
}
}
Output
[
MarketEstimate(year: 2000, price: 12830, mileage: 11041),
MarketEstimate(year: 2001, price: 12830, mileage: 11041),
]
Like this
[
MarketEstimate(year: 2000, mileage: 0, price: 12830),
MarketEstimate(year: 2000, mileage: 10000, price: 11041),
MarketEstimate(year: 2001, mileage: 0, price: 12830),
MarketEstimate(year: 2001, mileage: 10000, price: 11041),
]
You have to manipulate that json. Try:
let dict: [String: Any] = [
"market_estimates": [
"2000": [
"0": 12830,
"10000": 11041
],
"2001": [
"0": 12830,
"10000": 11041
]
]
]
// [
// {
// "year": 2000,
// "mileage": 0,
// "price": 12830
// },
// {
// "year": 2000,
// "mileage": 10000,
// "price": 11041
// },
// {
// "year": 2001,
// "mileage": 0,
// "price": 12830
// },
// {
// "year": 2001,
// "mileage": 10000,
// "price": 11041
// }
// ]
let jsonArray = (dict["market_estimates"] as? [String: [String: Int]] ?? [:])
.flatMap { yearString, mileageAndPrice in
return mileageAndPrice.flatMap { mileageString, price -> [String: Int]? in
guard let year = Int(yearString), let mileage = Int(mileageString) else { return nil }
return [
"year": year,
"mileage": mileage,
"price": price,
]
}
}
let estimates = Mapper<MarketEstimate>().mapArray(JSONArray: jsonArray)
print(estimates)
Do you have any alternative solution which could work for every dictionary of dictionaries? I'm using Firebase where every array is stored as dictionaries like in @mikkelam 's example.
I ended up writing a custom mapping method which tries to map array or dictionary to array. In dictionary case key is passed by using Context.
extension Map {
func valueFromArrayOrDictionary<Element: ImmutableMappable >(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [Element] {
let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter)
guard let JSONObject = currentValue else {
throw MapError(key: key, currentValue: currentValue, reason: "Found unexpected nil value", file: file, function: function, line: line)
}
return try Mapper<Element>().mapArrayOrDictionary(JSONObject: JSONObject)
}
fileprivate func currentValue(for key: String, nested: Bool? = nil, delimiter: String = ".") -> Any? {
let isNested = nested ?? key.contains(delimiter)
return self[key, nested: isNested, delimiter: delimiter].currentValue
}
}
extension Mapper where N: ImmutableMappable {
func mapArrayOrDictionary(JSONObject: Any) throws -> [N] {
if let dictionary = JSONObject as? [String: Any] {
return try dictionary.map { try N(JSONObject: $0.value, context: FirebaseContext(key: $0.key)) }
} else if let array = JSONObject as? [Any] {
return try array.map { try N(JSONObject: $0) }
} else {
throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: Any] or [Any]")
}
}
}
simple way:
class MarketEstimate {
var price: Int!
var mileage: Int!
}
class MarketEstimates {
var market_estimates:Dictionary<String, MarketEstimate >!
}
then you would have get your structure in market_estimates["2000"], but without the year inside the struct...
@altagir The reason I did not use a dictionary is because it is not supported by Realm.
I ended up writing a custom mapping method which tries to map array or dictionary to array. In dictionary case key is passed by using Context.
extension Map { func valueFromArrayOrDictionary<Element: ImmutableMappable >(_ key: String, nested: Bool? = nil, delimiter: String = ".", file: StaticString = #file, function: StaticString = #function, line: UInt = #line) throws -> [Element] { let currentValue = self.currentValue(for: key, nested: nested, delimiter: delimiter) guard let JSONObject = currentValue else { throw MapError(key: key, currentValue: currentValue, reason: "Found unexpected nil value", file: file, function: function, line: line) } return try Mapper<Element>().mapArrayOrDictionary(JSONObject: JSONObject) } fileprivate func currentValue(for key: String, nested: Bool? = nil, delimiter: String = ".") -> Any? { let isNested = nested ?? key.contains(delimiter) return self[key, nested: isNested, delimiter: delimiter].currentValue } } extension Mapper where N: ImmutableMappable { func mapArrayOrDictionary(JSONObject: Any) throws -> [N] { if let dictionary = JSONObject as? [String: Any] { return try dictionary.map { try N(JSONObject: $0.value, context: FirebaseContext(key: $0.key)) } } else if let array = JSONObject as? [Any] { return try array.map { try N(JSONObject: $0) } } else { throw MapError(key: nil, currentValue: JSONObject, reason: "Cannot cast to '[String: Any] or [Any]") } } }
Could you give an example how to use this extension?
Most helpful comment
I ended up writing a custom mapping method which tries to map array or dictionary to array. In dictionary case key is passed by using Context.