Given the JSON data:
{
"location": "Toronto, Canada",
"three_day_forecast": [
{
"conditions": "Partly cloudy",
"day" : "Monday",
"temperature": 20
},
{
"conditions": "Showers",
"day" : "Tuesday",
"temperature": 22
},
{
"conditions": "Sunny",
"day" : "Wednesday",
"temperature": 28
}
]
}
And the following classes:
class WeatherResponse: Mappable {
var location: String?
var threeDayForecast: [Forecast]?
required init?(_ map: Map){
}
func mapping(map: Map) {
location <- map["location"]
threeDayForecast <- map["three_day_forecast"]
}
}
class Forecast: Mappable {
var day: String?
var temperature: Int?
var conditions: String?
required init?(_ map: Map){
}
func mapping(map: Map) {
day <- map["day"]
temperature <- map["temperature"]
conditions <- map["conditions"]
}
}
I have in my ViewController:
class ViewController: UIViewController {
var threeDayForecastArray = [Forecast]()
override func viewDidLoad() {
super.viewDidLoad()
getWeatherReport()
print(threeDayForecastArray.count) // 0
print(threeDayForecastArray[0].conditions) //fatal error: Index out of range
}
func getWeatherReport() {
let URL = "https://raw.githubusercontent.com/tristanhimmelman/AlamofireObjectMapper/d8bb95982be8a11a2308e779bb9a9707ebe42ede/sample_json"
Alamofire.request(.GET, URL).responseObject { (response: Response<WeatherResponse, NSError>) in
let weatherResponse = response.result.value
print(weatherResponse?.location)
if let threeDayForecast = weatherResponse?.threeDayForecast {
self.threeDayForecastArray = threeDayForecast
for forecast in threeDayForecast {
print(forecast.day)
print(forecast.temperature)
}
}
}
}
}
I'm trying to populate threeDayForecastArray, but it's giving me count = 0 or an out of index error. Any advice on what I'm doing wrong or help are welcome.
Related
I'm trying to map JSON of an Array type to a Dictionary and i'm not quite sure how to do it using ObjectMapper.
Example JSON:
{
"colors": [
{
"id": "red",
"r": "255",
"g": "255",
"b": "255"
}
]
}
You could do the following. Map it to an array first then using the didSet map it to the dictionary.
class MyClass: Mappable {
private var arrayColors = [MyClass2] {
didSet {
var mapTypes = [String:MyClass2]?
for obj in arrayColors {
mapTypes[obj.id] = obj
}
types = mapTypes
}
}
var colors:[String:MyClass2] = [String:MyClass2]()
func mapping(map: Map) {
arrayColors <- map["colors"]
}
}
I started to use ObjectMapper this week and I'm trying to map a JSON into 2 CustomClasses but I don't known if ObjectMapper has some function to do what I want. First CustomClass has a property of type: [String:CustomClass2] where the index of this Dictionary should be property ID of second CustomObject.
JSON Used:
{
"types": [
{
"id": "mk8QPMSo2xvtSoP0cBUD",
"name": "type 1",
"img": "type_1",
"showCategories": false,
"modalityHint": [
"K7VqeFkRQNXoh2OBxgIf"
],
"categories": [
"mP3MqbJrO5Da1dVAPRvk",
"SlNezp2m3PECnTyqQMUV"
]
}
]
}
Classes Used:
class MyClass: Mappable {
var types:[String:MyClass2] = [String:MyClass2]() //Index should be ID property of MyClass2 Object
required init?(map:Map) {
guard map.JSON["types"] != nil else {
return nil
}
}
func mapping(map: Map) {
types <- map["types"]
}
}
class MyClass2: Mappable {
private var id: String!
private var name: String!
private var img: String!
private var showCategories: Bool!
private var modalityHint: [String]?
private var categories: [String]?
required init?(map: Map) { }
func mapping(map: Map) {
id <- map["id"]
name <- map["name"]
img <- map["img"]
showCategories <- map["showCategories"]
modalityHint <- map["modalityHint"]
categories <- map["categories"]
}
In your JSON the types key is an array not a Dictionary.
Change:
var types:[String:MyClass2] = [String:MyClass2]()
To:
var types:[Class2] = []
Like this:
class MyClass: Mappable {
private var arrayTypes = [MyClass2] {
didSet{
var mapTypes = [String:MyClass2]?
for obj in arrayTypes {
mapTypes[obj.id] = obj
}
types = mapTypes
}
}
var types:[String:MyClass2] = [String:MyClass2]()
required init?(map:Map) {
guard map.JSON["types"] != nil else {
return nil
}
}
func mapping(map: Map) {
arrayTypes <- map["types"]
}
}
I'm writing in Swift 3.1, using ObjectMapper to map my JSON response to my models.
I'm trying to map this rather complex JSON response with dynamic keys and am hoping to get some feedback on what I'm doing wrong.
A group has statistics about it's progress. It has stats broken down to years and then months. Each month within a year has results, ROI and win. The ROI and win are just percentages but the results key is fixed with the keys below, 1-5, and then some integer as a value.
My JSON
"stats": {
"2017": {
"1": {
"results": {
"1": 13,
"2": 3,
"3": 1,
"4": 1,
"5": 0
},
"roi": 0.40337966202464975,
"win": 0.8181818181818182
},
"2": {
"results": {
"1": 13,
"2": 5,
"3": 1,
"4": 2,
"5": 1
},
"roi": 0.26852551067922953,
"win": 0.717948717948718
}
}
}
My models
class GroupResponse: Mappable {
var stats: [String: [String: StatsMonthResponse]]?
func mapping(map: Map) {
stats <- map["stats"]
}
}
class StatsMonthResponse: Mappable {
var tips: [String: Int]?
var roi: Double?
var win: Double?
func mapping(map: Map) {
tips <- map["results"]
roi <- map["roi"]
win <- map["win"]
}
}
What I get
The response I get has the stats property in my GroupResponse class, as nil.
What other approach could I do to accomplish this, or change in my implementation to get this done?
Solution
I solved my problem by mapping the JSON manually.
class GroupResponse: Mappable {
var stats: [String: StatsYear]?
func mapping(map: Map) {
stats <- map["stats"]
}
}
class StatsYear: Mappable {
var months: [String: StatsMonth] = [:]
override func mapping(map: Map) {
for (monthKey, monthValue) in map.JSON as! [String: [String: Any]] {
let month = StatsMonth()
for (monthKeyType, valueKeyType) in monthValue {
if monthKeyType == "results" {
let tipResultDict = valueKeyType as! [String: Int]
for (result, tipsForResult) in tipResultDict {
month.tips[result] = tipsForResult
}
}
else if monthKeyType == "roi" {
month.roi = valueKeyType as? Double
}
else if monthKeyType == "win" {
month.win = valueKeyType as? Double
}
}
months[monthKey] = month
}
}
}
class StatsMonth {
var tips: [String: Int] = [:]
var roi: Double?
var win: Double?
}
There's probably a better solution to this problem but this is what I'm sticking with for now.
Hopefully this helps!
I'm trying to map following JSON using AlamofireObjectMapper
{
"res": [
{
"electiveClusterId": 25,
"multipleSelectionEnabled": false,
"electiveClusterName": null,
"coursesList": [
{
"courseId": 6,
"courseName": "Accounts",
"selected": false,
"classStarted": false
},
{
"courseId": 19,
"courseName": "Javascript",
"selected": false,
"classStarted": false
}
]
},
{
"electiveClusterId": 26,
"multipleSelectionEnabled": true,
"electiveClusterName": null,
"coursesList": [
{
"courseId": 11,
"courseName": "Algorithms and Logics",
"selected": false,
"classStarted": false
},
{
"courseId": 15,
"courseName": "Android App",
"selected": false,
"classStarted": false
}
]
}
]
}
Here are my respective object classes for those:
class ElectivesMainResponse: Mappable {
private var _res: [ElectivesResponse]?
var Res: [ElectivesResponse]? {
return _res
}
required init?(map: Map){
mapping(map: map)
}
func mapping(map: Map) {
_res <- map["res"]
}
}
class ElectivesResponse: Mappable {
private var _electiveClusterId: Int!
private var _multipleSelectionEnabled: Bool!
private var _electiveClusterName: String?
private var _courseList: [Course]!
var ClusterId: Int {
return _electiveClusterId
}
var MultipleSelectionEnabled: Bool {
return _multipleSelectionEnabled
}
var ElectiveClusterName: String? {
if let elective = _electiveClusterName {
return elective
} else {
return "Elective"
}
}
var CourseList: [Course]? {
return _courseList
}
required init?(map: Map){
}
func mapping(map: Map) {
_electiveClusterId <- map["electiveClusterId"]
_multipleSelectionEnabled <- map["multipleSelectionEnabled"]
_electiveClusterName <- map["electiveClusterName"]
_courseList <- map["coursesList"]
}
}
class Course: Mappable {
private var _courseId: Int!
private var _courseName: String!
private var _courseSelected: Bool!
private var _classStarted: Bool!
var CourseId: Int {
return _courseId
}
var CourseName: String {
return _courseName
}
var CourseSelected: Bool {
return _courseSelected
}
var ClassStarted: Bool {
return _classStarted
}
required init?(map: Map){
}
func mapping(map: Map) {
_courseId <- map["courseId"]
_courseName <- map["courseName"]
_courseSelected <- map["selected"]
_classStarted <- map["classStarted"]
}
}
But for some reason, In my responseObject I always get a nil object. Can someone please help me with where I am going wrong?
Here is the code for getting the response
func getElectivesCurriculumList(forProgramId programId: Int, batchYear: Int) {
let url = "someurl"
AlamofireManager.getAFSessionManager().request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: nil).responseObject { (response: DataResponse<ElectivesMainResponse>) in
print("Electives API URL: \(url)")
switch response.result {
case .success(_ ):
if let value = response.result.value {
print("\(value.Res?.count)") //this is always nil
self.onGetElectivesResponse(value)
}
break
case .failure(let error):
print(error.localizedDescription)
self.onGetElectivesFailure(error.localizedDescription)
break
}
}
}
I'm receiving the following json from the API:
[ { "first_id": 1,
"second_objs": [ { "second_id": 2,
"third_objs": [ { "third_id": 3,
"param": "abcd" }] } ] } ]
And serialising it using ObjectMapper:
class First: NSObject, Mappable {
var first_id: Int?
var second_objs: [Second]?
required init?(_ map: Map){}
func mapping(map: Map) {
first_id <- map["first_id"]
second_objs <- map["second_objs"]
}
}
class Second: NSObject, Mappable {
var second_id: Int?
var third_objs: [Third]?
required init?(_ map: Map){}
func mapping(map: Map) {
second_id <- map["second_id"]
third_objs <- map["third_objs"]
}
}
class Third: NSObject, Mappable {
var third_id: Int?
var param: String?
required init?(_ map: Map){}
func mapping(map: Map) {
third_id <- map["third_id"]
param <- map["param"]
}
}
Now after recieving and modifying that JSON I need to send it back to the API.
However before sending back I need to rename keys "second_objs" and "third_objs" to "something_else_2" and "something_else_3".
[ { "first_id": 1,
"something_else_2": [ { "second_id": 2,
"something_else_3": [ { "third_id": 3,
"param": "abcd" }] } ] } ]
How do I do the renaming in a clean way?