Monday, April 30, 2018

Swift parse JSON to Model Object

Leave a Comment

I had this method here and it returned my data exactly as I needed it:

func getTestData() -> [Any]?     {         return [GradingData(lot: "lot", andColumns: "andColumns", SLAIssuedFinalGrading: true, SLAIssuedFinalGradingDate: "SLAIssuedFinalGradingDate", CityApprovalIssued: true, CityCommentReceived: false, GradingRepair: "GradingRepair", CurbStopRepair: "CurbStopRepair", SplashPadDownSpout: "SplashPadDownSpout", RYCBOtherRepairs: "RYCBOtherRepairs", Comments: "Comments", columnCamera: "", DepositReceived: false), GradingData(lot: "lot", andColumns: "andColumns2", SLAIssuedFinalGrading: false, SLAIssuedFinalGradingDate: "SLAIssuedFinalGradingDate", CityApprovalIssued: false, CityCommentReceived: false, GradingRepair: "GradingRepair", CurbStopRepair: "CurbStopRepair", SplashPadDownSpout: "SplashPadDownSpout", RYCBOtherRepairs: "RYCBOtherRepairs", Comments: "Comments", columnCamera: "", DepositReceived: false)]     } 

Now I am trying to do a call to an API and return the exact same structure with this:

func GetLandGradingData(_ community: String, completion: @escaping (_ result: [GradingData]) -> Void)     {          let escapedCommunity = community.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)          let urlComponents = NSURLComponents(string: webservice + "?community=" + escapedCommunity!);         urlComponents?.user = appDelegate.username;         urlComponents?.password = appDelegate.password;          let url = urlComponents?.url;          let returnedData = [GradingData]()          URLSession.shared.dataTask(with: url!, completionHandler: {             (data, response, error) in              if(error != nil){                  completion(returnedData)              }else{                  do{                      let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [GradingData]                      OperationQueue.main.addOperation({                          completion(json)                      })                  }catch let error as NSError{                      print(error)                     completion(returnedData)                  }             }          }).resume()      } 

My problem is that is method is not returning the same structure as the getTestData method, attached are screenshots on what getTestData returns and what this api method call returns.

enter image description here enter image description here

Here is my whole class:

@objcMembers class GradingData : NSObject {      /**      Define lot Column String      */     var lot: String?     /**      Define Address Column String      */     var address: String?     /**      Define SLA Issued Final Grading Column String      */     var SLAIssuedFinalGrading = false     /**      Define SLA Issued Final Grading Date Column String      */     var SLAIssuedFinalGradingDate: String?     /**      Define City Approval Issued Column String      */     var CityApprovalIssued = false     /**      Define City Comment Received Column String      */     var CityCommentReceived = false     /**      Define Grading Repair Column String      */     var GradingRepair: String?     /**      Define Curb Stop Repair Column String      */     var CurbStopRepair: String?     /**      Define Splash Pad or Down Spout Column String      */     var SplashPadDownSpout: String?     /**      Define RYCB or Other Repairs Column String      */     var RYCBOtherRepairs: String?     /**      Define Comments Column String      */     var Comments: String?     /**      Define Camera Column String      */     var columnCamera: String?     /**      Define Deposit Received Column String      */     var DepositReceived = false       /**      Inital call to class      */     init(lot: String?, andColumns address: String?, SLAIssuedFinalGrading: Bool?, SLAIssuedFinalGradingDate: String?, CityApprovalIssued: Bool?, CityCommentReceived: Bool?, GradingRepair: String?, CurbStopRepair: String?, SplashPadDownSpout: String?, RYCBOtherRepairs: String?, Comments: String?, columnCamera: String?, DepositReceived: Bool?) {          super.init()          //Set lot string          self.lot = lot          //Set Address Column string          self.address = address          //Set SLA Issued Final Grading Column string          self.SLAIssuedFinalGrading = SLAIssuedFinalGrading!          //Set SLA Issued Final Grading Date Column string          self.SLAIssuedFinalGradingDate = SLAIssuedFinalGradingDate          //Set City Approval Issued Column string          self.CityApprovalIssued = CityApprovalIssued!          //Set City Comment Received Column string          self.CityCommentReceived = CityCommentReceived!          //Set Grading Repair Column string          self.GradingRepair = GradingRepair          //Set Curb Stop Repair Column string          self.CurbStopRepair = CurbStopRepair          //Set Splash Pad or Down Spout Column string          self.SplashPadDownSpout = SplashPadDownSpout          //Set RYCB or Other Repairs Column string          self.RYCBOtherRepairs = RYCBOtherRepairs          //Set Comments Column string          self.Comments = Comments          //Set Camera Column string          self.columnCamera = columnCamera          //Set Deposit Received Column string          self.DepositReceived = DepositReceived!      } } 

Here is my Data from the API:

<Reports> <CityApprovalIssued>false</CityApprovalIssued> <CityCommentReceived>false</CityCommentReceived> <Comments></Comments> <CurbStopRepair></CurbStopRepair> <DepositReceived>false</DepositReceived> <GradingRepair></GradingRepair> <RYCBOtherRepairs></RYCBOtherRepairs> <SLAIssuedFinalGrading>false</SLAIssuedFinalGrading> <SLAIssuedFinalGradingDate/> <SplashPadDownSpout></SplashPadDownSpout> <address>123 Fake Street</address> <lot>A0001</lot> </Reports> <Reports> <CityApprovalIssued>false</CityApprovalIssued> <CityCommentReceived>false</CityCommentReceived> <Comments></Comments> <CurbStopRepair></CurbStopRepair> <DepositReceived>false</DepositReceived> <GradingRepair></GradingRepair> <RYCBOtherRepairs></RYCBOtherRepairs> <SLAIssuedFinalGrading>false</SLAIssuedFinalGrading> <SLAIssuedFinalGradingDate/> <SplashPadDownSpout></SplashPadDownSpout> <address>125 Fake Street</address> <lot>A0002</lot> </Reports> 

This is from an ASP.NET MVC API Controller: <ArrayOfReports xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/App.Models">

This is what I get when I do this:

let string = String(data: data!, encoding: .utf8)  print(string as Any)  Optional("[{\"lot\":\"A0001\",\"address\”:\”123 Fake Street\”,\”SLAIssuedFinalGrading\":false,\"SLAIssuedFinalGradingDate\":\"\",\"CityApprovalIssued\":false,\"CityCommentReceived\":false,\"GradingRepair\":\"                                                                                                                                                                                                                                                               \",\"CurbStopRepair\":\"                                                                                                                                                                                                                                                               \",\"SplashPadDownSpout\":\"                                                                                                                                                                                                                                                               \",\"RYCBOtherRepairs\":\"                                                                                                                                                                                                                                                               \",\"Comments\":\"                                                                                                                                                                                                                                                               \",\"DepositReceived\":false},{\"lot\":\"A0002\",\"address\":\"125 Fake Street\",\"SLAIssuedFinalGrading\":false,\"SLAIssuedFinalGradingDate\":\"\",\"CityApprovalIssued\":false,\"CityCommentReceived\":false,\"GradingRepair\":\"                                                                                                                                                                                                                                                               \",\"CurbStopRepair\":\"                                                                                                                                                                                                                                                               \",\"SplashPadDownSpout\":\"                                                                                                                                                                                                                                                               \",\"RYCBOtherRepairs\":\"                                                                                                                                                                                                                                                               \",\"Comments\":\"                                                                                                                                                                                                                                                               \",\"DepositReceived\":false}]") 

4 Answers

Answers 1

First of all the response data is indeed JSON, the XML output is a bit confusing.

You probably don't need a class inherited from NSObject, in most cases especially in iOS a struct is sufficient.

Except the key columnCamera all keys exist in all records, so declare all properties as non-optional without a default value and columnCamera as optional, and declare all properties as constants (let) if they are not going to change.

An explicit init method is not needed.

The CodingKeys are added to translate most of the keys to lowercase.

struct GradingData : Decodable {      private enum CodingKeys : String, CodingKey {         case lot, address, SLAIssuedFinalGrading         case SLAIssuedFinalGradingDate         case cityApprovalIssued = "CityApprovalIssued"         case cityCommentReceived = "CityCommentReceived"         case gradingRepair = "GradingRepair"         case curbStopRepair = "CurbStopRepair"         case splashPadDownSpout = "SplashPadDownSpout"         case RYCBOtherRepairs, comments = "Comments"         case columnCamera, depositReceived = "DepositReceived"     }      let lot: String     let address: String     let SLAIssuedFinalGrading : Bool     let SLAIssuedFinalGradingDate: String     let cityApprovalIssued : Bool     let cityCommentReceived : Bool     let gradingRepair : String     let curbStopRepair : String     let splashPadDownSpout : String     let RYCBOtherRepairs : String     let comments : String     let columnCamera : String?     let depositReceived : Bool  } 

Then decode the JSON

....  var returnedData = [GradingData]() URLSession.shared.dataTask(with: url!, completionHandler: {(data, response, error) in      if error != nil {          print(error!)          completion(returnedData)      } else {          do {             returnedData = try JSONDecoder().decode([GradingData].self, from: data!)             DispatchQueue.main.async {                 completion(returnedData)             }          } catch {              print(error)              completion(returnedData)          }      }  }).resume() 

Answers 2

Just add another init into your GradingData model class like this:

convenience init(data: [String : Any]) {  init(lot: data["lot"] as? String,  andColumns: data["address"] as? String,  SLAIssuedFinalGrading: data["SLAIssuedFinalGrading"] as? Bool,  SLAIssuedFinalGradingDate: data["SLAIssuedFinalGradingDate"] as? String,   CityApprovalIssued: data["CityApprovalIssued"] as? Bool,  CityCommentReceived: data["CityCommentReceived"] as? Bool,  GradingRepair: data["GradingRepair"] as? String,  CurbStopRepair: data["CurbStopRepair"] as? String,  SplashPadDownSpout: data["SplashPadDownSpout"] as? String,  RYCBOtherRepairs: data["RYCBOtherRepairs"] as? String,  Comments: data["Comments"] as? String,  columnCamera: data["columnCamera"] as? String,  DepositReceived: data["DepositReceived"] as? Bool) } 

And in func GetLandGradingData(...) method replace inside do { } braces with these:

let json = try JSONSerialization.jsonObject(with: data!, options:.allowFragments) as! [[String : Any]]                      OperationQueue.main.addOperation({                        let gradingDatas = json.map { GradingData(data: $0) }                          completion(gradingDatas)                      }) 

Answers 3

Just because you asked for an example (but did not provide your GradingData which will make this really abstract):

struct GradingData : Codable {     // here is the meat, but you did not show ... }  ...  let grades = JSONDecoder().decode(GradingData.self, from: data) 

Swift is the first language I have come across which has nailed this. It really just boils down to implementing an interface (without actually doing anything interesting most of the time while still providing the possibility to do all the necessary things with minimal effort) and get a free JSON parser for your objects (not just some hash-junk dictionary).

(It is called "progress" :-)

Answers 4

Convert XML to dictionary by custom class or nsxmlparser. or if u have array of Grading then

var modelArr = [Grading]() 

var model = Grading.init(fromDictionary dictionary: xmlParsedDic) modelArr.append(model)

use this model class

import Foundation 

class Grading : NSObject, NSCoding {

var cityApprovalIssued : Bool! var cityCommentReceived : Bool! var comments : String! var curbStopRepair : String! var depositReceived : Bool! var gradingRepair : String! var rYCBOtherRepairs : String! var sLAIssuedFinalGrading : Bool! var sLAIssuedFinalGradingDate : String! var splashPadDownSpout : String! var address : String! var lot : String!   init(fromDictionary dictionary: [String:Any]){     cityApprovalIssued = dictionary["CityApprovalIssued"] as? Bool     cityCommentReceived = dictionary["CityCommentReceived"] as? Bool     comments = dictionary["Comments"] as? String     curbStopRepair = dictionary["CurbStopRepair"] as? String     depositReceived = dictionary["DepositReceived"] as? Bool     gradingRepair = dictionary["GradingRepair"] as? String     rYCBOtherRepairs = dictionary["RYCBOtherRepairs"] as? String     sLAIssuedFinalGrading = dictionary["SLAIssuedFinalGrading"] as? Bool     sLAIssuedFinalGradingDate = dictionary["SLAIssuedFinalGradingDate"] as? String     splashPadDownSpout = dictionary["SplashPadDownSpout"] as? String     address = dictionary["address"] as? String     lot = dictionary["lot"] as? String }  func toDictionary() -> [String:Any] {     var dictionary = [String:Any]()     if cityApprovalIssued != nil{         dictionary["CityApprovalIssued"] = cityApprovalIssued     }     if cityCommentReceived != nil{         dictionary["CityCommentReceived"] = cityCommentReceived     }     if comments != nil{         dictionary["Comments"] = comments     }     if curbStopRepair != nil{         dictionary["CurbStopRepair"] = curbStopRepair     }     if depositReceived != nil{         dictionary["DepositReceived"] = depositReceived     }     if gradingRepair != nil{         dictionary["GradingRepair"] = gradingRepair     }     if rYCBOtherRepairs != nil{         dictionary["RYCBOtherRepairs"] = rYCBOtherRepairs     }     if sLAIssuedFinalGrading != nil{         dictionary["SLAIssuedFinalGrading"] = sLAIssuedFinalGrading     }     if sLAIssuedFinalGradingDate != nil{         dictionary["SLAIssuedFinalGradingDate"] = sLAIssuedFinalGradingDate     }     if splashPadDownSpout != nil{         dictionary["SplashPadDownSpout"] = splashPadDownSpout     }     if address != nil{         dictionary["address"] = address     }     if lot != nil{         dictionary["lot"] = lot     }     return dictionary }  @objc required init(coder aDecoder: NSCoder) {      cityApprovalIssued = aDecoder.decodeObject(forKey: "CityApprovalIssued") as? Bool      cityCommentReceived = aDecoder.decodeObject(forKey: "CityCommentReceived") as? Bool      comments = aDecoder.decodeObject(forKey: "Comments") as? String      curbStopRepair = aDecoder.decodeObject(forKey: "CurbStopRepair") as? String      depositReceived = aDecoder.decodeObject(forKey: "DepositReceived") as? Bool      gradingRepair = aDecoder.decodeObject(forKey: "GradingRepair") as? String      rYCBOtherRepairs = aDecoder.decodeObject(forKey: "RYCBOtherRepairs") as? String      sLAIssuedFinalGrading = aDecoder.decodeObject(forKey: "SLAIssuedFinalGrading") as? Bool      sLAIssuedFinalGradingDate = aDecoder.decodeObject(forKey: "SLAIssuedFinalGradingDate") as? String      splashPadDownSpout = aDecoder.decodeObject(forKey: "SplashPadDownSpout") as? String      address = aDecoder.decodeObject(forKey: "address") as? String      lot = aDecoder.decodeObject(forKey: "lot") as? String  }  @objc func encode(with aCoder: NSCoder) {     if cityApprovalIssued != nil{         aCoder.encode(cityApprovalIssued, forKey: "CityApprovalIssued")     }     if cityCommentReceived != nil{         aCoder.encode(cityCommentReceived, forKey: "CityCommentReceived")     }     if comments != nil{         aCoder.encode(comments, forKey: "Comments")     }     if curbStopRepair != nil{         aCoder.encode(curbStopRepair, forKey: "CurbStopRepair")     }     if depositReceived != nil{         aCoder.encode(depositReceived, forKey: "DepositReceived")     }     if gradingRepair != nil{         aCoder.encode(gradingRepair, forKey: "GradingRepair")     }     if rYCBOtherRepairs != nil{         aCoder.encode(rYCBOtherRepairs, forKey: "RYCBOtherRepairs")     }     if sLAIssuedFinalGrading != nil{         aCoder.encode(sLAIssuedFinalGrading, forKey: "SLAIssuedFinalGrading")     }     if sLAIssuedFinalGradingDate != nil{         aCoder.encode(sLAIssuedFinalGradingDate, forKey: "SLAIssuedFinalGradingDate")     }     if splashPadDownSpout != nil{         aCoder.encode(splashPadDownSpout, forKey: "SplashPadDownSpout")     }     if address != nil{         aCoder.encode(address, forKey: "address")     }     if lot != nil{         aCoder.encode(lot, forKey: "lot")     }  }} 
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment