3

I am using NSJSONSerialization in Swift 1.2 to parse some json that is returned from an API response.

var err: NSError?
let opts = NSJSONReadingOptions.AllowFragments
let json: AnyObject? = NSJSONSerialization.JSONObjectWithData(jsonData!, options: opts, error: &err)

The parsed json is provided as AnyObject?. I would like to use this optional to initialize a class object which can be used as the model data in an application.

class Alerts {
    let type: String
    let date: String
    let description: String
    let expires: String
    let message: String

    init(json: AnyObject) {
        if let
        jsonDict = json as? [String: AnyObject],
        alertsArray = jsonDict["alerts"] as? [AnyObject],
        alertsDict = alertsArray[0] as? [String: AnyObject],
        type = alertsDict["type"] as? String,
        date = alertsDict["date"] as? String,
        description = alertsDict["description"] as? String,
        expires = alertsDict["expires"] as? String,
        message = alertsDict["message"] as? String
        {
            self.type = type
            self.date = date
            self.description = description
            self.expires = expires
            self.message = message
        }
        else
        {
            self.type = "err"
            self.date = "err"
            self.description = "err"
            self.expires = "err"
            self.message = "err"
        }
    }
}

// example of creating a data model from the json
let alerts = Alerts(json: json!)
alerts.type
alerts.date
alerts.description
alerts.expires
alerts.message

Since NSJSONSerialization returns an optional, I have to check for the existence of each value type as I extract the json data. As you can see in the above code, I used the improved optional bindings from Swift 1.2 to clean up the init method. Without using third-party libraries, is there anything else I can do to the class model (enums, structs, type aliases) to make it more readable? Should I use a struct for the model data instead of a class? Would it be possible to create a custom type using an enum or struct to represent a json object?

wigging
  • 8,492
  • 12
  • 75
  • 117
  • I know you mentioned you didn't want to use third party libraries but I really think you should take a look at SwiftyJSON. It checks everything for you and gives you a nested dictionary. Personally, I would then use this to map the JSON to a swift model using Structs, assingning data in the init and nesting the Structs appropriating staring from something like "Root()" and going down from there. – Cole Jul 23 '15 at 02:17
  • @Cole I'm aware of SwiftyJSON and Argo, but I do not want to depend on them. Using a `struct` instead of a `class` for the swift model may be a good idea though. – wigging Jul 23 '15 at 03:41

1 Answers1

0

So without using third party libraries, the if let trees are usually the best practice, which you have shown. To help you later down the road, maybe recreate your object hierarchy in JSON as a Struct model in Swift. Something like:

var json = JSON(JSONData.sharedjson.jsonRaw!)
var mongoIdTest = json["resultset"]["account"]["mongoId"].string

struct Root {
    var timestamp: Int?
    var resultset = ResultSet()

    init() {
        self.timestamp = json["timestamp"].int
        println(json)
    }

}

struct ResultSet {
    var alert: String?

    var account = Account()
    var customer = Customer()

    init() {

    }


}

struct Account {
    var mongoId: String?

    init() {
        mongoId = json["resultset"]["account"]["mongoId"].string
    }
}

struct Locations {

}

struct Customer {
    var account: String?
    var address: String?
    var id: String?
    var loginId: String?
    var mongoId: String?
    var name: String?

    var opco = Opco()

    init() {
        account = json["resultset"]["customer"]["account"].string
        address = json["resultset"]["customer"]["address"].string
        id = json["resultset"]["customer"]["id"].string
        loginId = json["resultset"]["customer"]["loginId"].string
        mongoId = json["resultset"]["customer"]["mongoId"].string
        name = json["resultset"]["customer"]["name"].string
    }

}

struct Opco {
    var id: String?
    var phone: String?
    var cutOffTime: String?
    var name: String?
    var payerId: String?

    init() {
        id = json["resultset"]["customer"]["opco"]["id"].string
        phone = json["resultset"]["customer"]["opco"]["phone"].string
        cutOffTime = json["resultset"]["customer"]["opco"]["cutOffTime"].string
        name = json["resultset"]["customer"]["opco"]["name"].string
        payerId = json["resultset"]["customer"]["opco"]["payerId"].string
    }
}

This way you can still use autocomplete and dot notation to navigate through your hierarchy.

Edit: I have a data structure from an actual project I've worked on added to the answer, hopefully this gives a better idea. Keep in mind that I'm using SwiftyJSON for the JSON() call.

Edit 2:

This is a method I found for getting JSON info into a Swift dictionary without the use of some other library. I'm not sure there is another way to do it that's easier without the use of third party libraries.

var urlToRequest = "https://EXAMPLE.com/api/account.login?username=MY_USERNAME&password=Hunter2"

if let json = NSData(contentsOfURL: NSURL(string: urlToRequest)!) {

    // Parse JSON to Dictionary
    var error: NSError?
    let boardsDictionary = NSJSONSerialization.JSONObjectWithData(json, options: NSJSONReadingOptions.MutableContainers, error: &error) as? Dictionary<String, AnyObject>
    fulljson = boardsDictionary

    // Display all keys and values
    println("Keys in User Data:")
    for (key, value) in boardsDictionary! {
        println("\(key)-------\(value)")
    }

    println(fulljson?["resultset"])
}
else {
    println("Test JSON nil: No Connection?")
}

That dictionary will be the input for your Structs.

Cole
  • 2,641
  • 1
  • 16
  • 34
  • Can elaborate on what you mean by using a `struct` for the object hierarchy? – wigging Jul 24 '15 at 00:24
  • You're still using a third-party library, SwiftyJSON. As my question stated, I would like to avoid using someone else's library to parse json. – wigging Jul 26 '15 at 15:06
  • I know, that was only a sample of using structs. In their initializers, you can assign values any way you want, I just used SwiftyJSON because that's how I originally wrote it. Regardless, `mongoId = json["resultset"]["account"]["mongoId"].string` could easily be `mongoId = JSONDictionary["resultset"]["account"]["mongoId"]` where `let JSONDictionary: Dictionary = NSJSONSerialization.JSONObjectWithData(JSONData, options: nil, error: &error) as NSDictionary` – Cole Jul 26 '15 at 15:13
  • The point is to turn a Dictionary into easily readable and accessible data. See this link for more info on parsing JSON directly into a dictionary. http://stackoverflow.com/questions/24310324/deserialize-json-nsdictionary-to-swift-objects – Cole Jul 26 '15 at 15:16
  • All of the examples in the link use third-party libraries too. I'm starting to think that there is just no easy way to handle json in Swift. – wigging Jul 26 '15 at 18:28
  • I've tried one more thing that may work for you. Check my answer for more details. – Cole Jul 26 '15 at 21:04