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
}
}
}
Related
I have the following situation:
struct RequestCommand: Codable {
var event: String
var uuid: String
var type: String
var data: [String]
}
var data = try! JSONEncoder().encode(RequestCommand(event: "click", uuid: "123456", type: "Button", data: ["A", "B", "C"]))
print(String(data: data, encoding: String.Encoding.utf8)!)
The result of the print statement is the following:
{
"data": [
"A",
"B",
"C"
],
"event": "click",
"type": "Button",
"uuid": "123456"
}
As you can see the order of the keys in the JSON isn't the same as the fields in the RequestCommand.
I know that JSONEncoder doesn't guarantee the order, also using .sortedKeys would not work for me since I must keep the exact order of the fields from the RequestCommand and I cannot rename them.
My question is, is there a library in swift that gives me the option of keeping the exact same order from the swift object in the JSON?
for (key, value) in OrderedDictionary will maintain the order.
struct OrderedDictionary<KeyType:Hashable, ValueType> {
private var _dictionary:Dictionary<KeyType, ValueType>
private var _keys:Array<KeyType>
init() {
_dictionary = [:]
_keys = []
}
init(minimumCapacity:Int) {
_dictionary = Dictionary<KeyType, ValueType>(minimumCapacity:minimumCapacity)
_keys = Array<KeyType>()
}
init(_ dictionary:Dictionary<KeyType, ValueType>) {
_dictionary = dictionary
_keys = map(dictionary.keys) { $0 }
}
subscript(key:KeyType) -> ValueType? {
get {
return _dictionary[key]
}
set {
if newValue == nil {
self.removeValueForKey(key)
}
else {
self.updateValue(newValue!, forKey: key)
}
}
}
mutating func updateValue(value:ValueType, forKey key:KeyType) -> ValueType? {
let oldValue = _dictionary.updateValue(value, forKey: key)
if oldValue == nil {
_keys.append(key)
}
return oldValue
}
mutating func removeValueForKey(key:KeyType) {
_keys = _keys.filter { $0 != key }
_dictionary.removeValueForKey(key)
}
mutating func removeAll(keepCapacity:Int) {
_keys = []
_dictionary = Dictionary<KeyType,ValueType>(minimumCapacity: keepCapacity)
}
var count: Int { get { return _dictionary.count } }
// keys isn't lazy evaluated because it's just an array anyway
var keys:[KeyType] { get { return _keys } }
// values is lazy evaluated because of the dictionary lookup and creating a new array
var values:GeneratorOf<ValueType> {
get {
var index = 0
return GeneratorOf<ValueType> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return self._dictionary[key]
}
}
}
}
}
extension OrderedDictionary : SequenceType {
func generate() -> GeneratorOf<(KeyType, ValueType)> {
var index = 0
return GeneratorOf<(KeyType, ValueType)> {
if index >= self._keys.count {
return nil
}
else {
let key = self._keys[index]
index++
return (key, self._dictionary[key]!)
}
}
}
}
func ==<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys == rhs._keys && lhs._dictionary == rhs._dictionary
}
func !=<Key: Equatable, Value: Equatable>(lhs: OrderedDictionary<Key, Value>, rhs: OrderedDictionary<Key, Value>) -> Bool {
return lhs._keys != rhs._keys || lhs._dictionary != rhs._dictionary
}
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 have the following JSON, which I am using together with ObjectMapper:
Open Api
Response snippet
{
"data": [
{
"CategoryName": "רוגע",
"CategoryID": "63",
"CategoryDate": "2016-08-26 02:12:05",
"CategoryImage": "relax.png",
"SubCategoryArray": [
{
"SubCategoryName": "רוגע",
"SubCategoryRefID": "63",
"SubCategoryID": "86",
"SubCategoryDate": "2016-08-28 02:57:07",
"TextArray": [
{
"TextID": "32",
"Text": "<p dir=\"rtl\"><span style=\"font-size:48px\"><strong><span dir=\"RTL\" lang=\"HE\" style=\"font-family:Arial\">פרופורציה</span></strong> . הכול הבל הבלים. חולף כהרף עין. אז לנשום.</span></p>\r\n"
},
My problem is getting the data from "SubCategoryArray", and "TextArray"
I tried to do the following in my mapping:
import UIKit
import ObjectMapper
class APIResult: Mappable {
var data : [dataArray]?
required init?(map: Map){
}
func mapping(map: Map) {
data <- map["data"]
}
}
class dataArray: Mappable{
var CategoryName: String?
var CategoryID: String?
var CategoryDate: String?
var CategoryImage: String?
var SubCategoryArray: SubCategoryArray?
required init?(map: Map){
}
func mapping(map: Map) {
CategoryName <- map["CategoryName"]
CategoryID <- map["CategoryID"]
CategoryDate <- map["CategoryDate"]
CategoryImage <- map["CategoryImage"]
SubCategoryArray <- map["SubCategoryArray"]
}
}
class SubCategoryArray: Mappable {
var SubCategoryName: String?
var SubCategoryRefID: String?
var SubCategoryID: String?
var SubCategoryDate: String?
var TextArray: TextArray?
required init?(map: Map){
}
func mapping(map: Map) {
SubCategoryName <- map["SubCategoryName"]
SubCategoryRefID <- map["SubCategoryRefID"]
SubCategoryID <- map["SubCategoryID"]
SubCategoryDate <- map["SubCategoryDate"]
TextArray <- map["TextArray"]
}
}
class TextArray: Mappable {
var TextID: String?
var Text:String?
required init?(map: Map){
}
func mapping(map: Map) {
TextID <- map["TextID"]
Text <- map["Text"]
// SubCategoryID <- map["SubCategoryID"]
// SubCategoryDate <- map["SubCategoryDate"]
// TextArray <- map["TextArray"]
}
}
Please point what I am doing wrong.
This is how you would map this data
import Foundation
import ObjectMapper
class customData: Mappable {
var categoryName: String = ""
var categoryId: String = ""
var subCategoryArray: [SubCategoryArray] = []
required init?(_ map: Map) {
}
func mapping(map: Map) {
categoryName <- map["data.CategoryName"]
categoryId <- map["data.CategoryID"]
subCategoryArray <- map["data.SubCategoryArray"]
}
}
class SubCategoryArray: Mappable {
var SubCategoryName: String = ""
var SubCategoryRefID: String = ""
var textArray: [TextArray] = []
required init?(_ map: Map) {
}
func mapping(map: Map) {
SubCategoryName <- map["SubCategoryName"]
SubCategoryRefID <- map["SubCategoryRefID"]
textArray <- map["TextArray"]
}
}
class TextArray: Mappable {
var TextID: String = ""
var Text: String = ""
required init?(_ map: Map) {
}
func mapping(map: Map) {
TextID <- map["TextID"]
Text <- map["Text"]
}
}
Let me know if you find any difficulty.
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.
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?