How to create objects from SwiftyJSON

后端 未结 4 1137
面向向阳花
面向向阳花 2021-02-19 06:23

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 ?

<         


        
相关标签:
4条回答
  • 2021-02-19 06:49

    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
    0 讨论(0)
  • 2021-02-19 06:52

    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.

    0 讨论(0)
  • 2021-02-19 06:54
    for (item, content) in hoge {
        let level = content["level"].intValue
    }
    

    that should work

    0 讨论(0)
  • 2021-02-19 07:04

    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

    0 讨论(0)
提交回复
热议问题