How to create objects from SwiftyJSON

匿名 (未验证) 提交于 2019-12-03 03:06:01

问题:

I have a code, that parses JSON's list of questions and I can get every property. How can I iterate through the whole file and for each question create an object ?

class ViewController: UIViewController {  var hoge: JSON?  override func viewDidLoad() {     super.viewDidLoad()      let number = arc4random_uniform(1000)     let url = NSURL(string: "http://www.wirehead.ru/try-en.json?\(number)")     var request = NSURLRequest(URL: url!)     var data = NSURLConnection.sendSynchronousRequest(request, returningResponse: nil, error: nil)     if data != nil {         hoge = JSON(data: data!)         let level = hoge!["pack1"][0]["level"].intValue         let questionText = hoge!["pack1"][0]["questionText"].stringValue         let answer1 = hoge!["pack1"][0]["answer1"].stringValue         let answer2 = hoge!["pack1"][0]["answer2"].stringValue         let answer3 = hoge!["pack1"][0]["answer3"].stringValue         let answer4 = hoge!["pack1"][0]["answer4"].stringValue         let correctAnswer = hoge!["pack1"][0]["correctAnswer"].stringValue         let haveAnswered = hoge!["pack1"][0]["haveAnswered"].boolValue      }   } } 

my model of Question which objects I want to create below

class Question {      var level : Int?     var questionText : String?     var answer1 : String?     var answer2 : String?     var answer3 : String?     var answer4 : String?     var correctAnswer : String?     var haveAnswered : Bool = false      init(level: Int, questionText:String, answer1:String, answer2:String, answer3:String, answer4:String, correctAnswer: String, haveAnswered:Bool) {         self.level = level         self.questionText = questionText         self.answer1 = answer1         self.answer2 = answer2         self.answer3 = answer3         self.answer4 = answer4         self.correctAnswer = correctAnswer         self.haveAnswered = false     }  } 

回答1:

This is how I would approach the problem.

Step 1

Since your init inside Question does receive non optional objects, I had the feeling that the properties of Questions should be non optional too. I also converted the properties from var to let (tell me if I am wrong).

Step 2

This is the refactored Question class. As you can see I added a class method build that receive a JSON (a SwiftyJSON) and returns a Question (if the json contains correct data), nil otherwise.

Right now I cannot do this with a failable initializer.

extension String {     func toBool() -> Bool? {         switch self.lowercaseString {         case "true", "1", "yes" : return true         case "false", "0", "no" : return false         default: return nil         }     } }  class Question {      let level: Int     let questionText: String     let answer1: String     let answer2: String     let answer3: String     let answer4: String     let correctAnswer: String     let haveAnswered: Bool      init(level: Int, questionText:String, answer1:String, answer2:String, answer3:String, answer4:String, correctAnswer: String, haveAnswered:Bool) {         self.level = level         self.questionText = questionText         self.answer1 = answer1         self.answer2 = answer2         self.answer3 = answer3         self.answer4 = answer4         self.correctAnswer = correctAnswer         self.haveAnswered = false     }      class func build(json:JSON) -> Question? {         if let             level = json["level"].string?.toInt(),             questionText = json["questionText"].string,             answer1 = json["answer1"].string,             answer2 = json["answer2"].string,             answer3 = json["answer3"].string,             answer4 = json["answer4"].string,             correctAnswer = json["correctAnswer"].string,             haveAnswered = json["haveAnswered"].string?.toBool() {                 return Question(                     level: level,                     questionText: questionText,                     answer1: answer1,                     answer2: answer2,                     answer3: answer3,                     answer4: answer4,                     correctAnswer: correctAnswer,                     haveAnswered: haveAnswered)         } else {             debugPrintln("bad json \(json)")             return nil         }     } } 

Step 3

Now let's look at viewDidLoad.

func viewDidLoad() {     super.viewDidLoad()      let number = arc4random_uniform(1000)      if let         url = NSURL(string: "http://www.wirehead.ru/try-en.json?\(number)"),         data = NSURLConnection.sendSynchronousRequest(NSURLRequest(URL: url), returningResponse: nil, error: nil) {         // line #a         let rootJSON = JSON(data: data)          // line #b         if let questions = (rootJSON["pack1"].array?.map { return Question.build($0) }) {             // now you have an array of optional questions [Question?]...         }      } } 

At line #a I put inside rootJSON the whole data received from the connection (converted into JSON).

What happen at line #b?

Well I try to access the array located inside "pack1".

rootJSON["pack1"].array? 

If the array exists I run the map method. This will extract each cell of the array and I will be able to refer to it with the $0 parameter name inside the closure.

Inside the closure I use this json block (that should represent a question) to build a Question instance.

The result will be an array of Question?. There could be ill values if some son data was not valid. If you want I can show you how to remove the nil values from this array

I could not try the code with real data, hope this helps.



回答2:

Step 1. We will create one protocol with one constructor method in it and Model class

protocol JSONable {     init?(parameter: JSON) }  class Style: JSONable {     let ID              :String!     let name            :String!      required init(parameter: JSON) {         ID            = parameter["id"].stringValue         name          = parameter["name"].stringValue     }      /*  JSON response format     {       "status": true,       "message": "",       "data": [         {           "id": 1,           "name": "Style 1"         },         {           "id": 2,           "name": "Style 2"         },         {           "id": 3,           "name": "Style 3"         }       ]     }     */ } 

Step 2. We will create extension of JSON which will convert JSON to model class type object

extension JSON {     func to<T>(type: T?) -> Any? {         if let baseObj = type as? JSONable.Type {             if self.type == .array {                 var arrObject: [Any] = []                 for obj in self.arrayValue {                     let object = baseObj.init(parameter: obj)                     arrObject.append(object!)                 }                 return arrObject             } else {                 let object = baseObj.init(parameter: self)                 return object!             }         }         return nil     } } 

Step 3. Use code with Alamofire or other code.

Alamofire.request(.GET, url).validate().responseJSON { response in         switch response.result {             case .success(let value):                 let json = JSON(value)                  var styles: [Style] = []                 if let styleArr = json["data"].to(type: Style.self) {                     styles = styleArr as! [Style]                 }                 print("styles: \(styles)")             case .failure(let error):                 print(error)         }  } 

I hope this will be useful.

Please refer to this link for more information on this.
https://github.com/SwiftyJSON/SwiftyJSON/issues/714



回答3:

You can use SwiftyJSONModel which was specifically designed for this purpose. So in your case the model would be like this:

class Question: JSONObjectInitializable {     enum PropertyKey: String {         case level, questionText         case answer1, answer2, answer3, answer4         case correctAnswer, haveAnswered     }      var level : Int?     var questionText : String?     var answer1 : String?     var answer2 : String?     var answer3 : String?     var answer4 : String?     var correctAnswer : String?     var haveAnswered : Bool = false      required init(object: JSONObject<PropertyKey>) throws {         level = object.value(for: .level)         questionText = object.value(for: .questionText)         answer1 = object.value(for: .answer1)         answer2 = object.value(for: .answer2)         answer3 = object.value(for: .answer3)         answer4 = object.value(for: .answer4)         correctAnswer = object.value(for: .correctAnswer)         haveAnswered = object.value(for: .haveAnswered) ?? false     }    } 

And then do like this:

let rootJSON = JSON(data: data) let questions = rootJSON.arrayValue.flatMap { try? Question(json: $0) } 

The framework gives you several nice features:

  1. All the keys are stored in separated enum PropertyKey
  2. No boilerplate as stringValue, intValue etc.
  3. If JSON will be invalid, framework will give a verbose error and you will immediately see what exactly went wrong


回答4:

for (item, content) in hoge {     let level = content["level"].intValue } 

that should work



易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!