(Swift) How to parse JSON with struct? - json

I want to parse JSON with struct and name it.
here is the JSON data:
{
"sgList": [
{
"ID": 11113,
"Name": "soss",
"Price": "10.0000",
"BigImagesUrl": "http://192.165.1.19:886/img/1/2015/7/11/20157111429315728.png",
"SmallImagesUrl": "http://192.165.1.19:886/img/1/2015/7/11/20157111429315728.png"
},
{
"ID": 11958,
"Name": "1017p-02",
"Price": "0.0000",
"BigImagesUrl": "http://192.165.1.13:886/img/rar-upload/f82f22ce-4a33-4ba2-a31d-4bae473f5d48/pics/797_1.jpg",
"SmallImagesUrl": "http://192.165.1.13:886/img/rar-upload/f82f22ce-4a33-4ba2-a31d-4bae473f5d48/pics/797_1-[135-135].jpg"
}
]
}
I spend hours on it and get nothing!
Please help me, Thank you very much!

If you don't want an third party library and do it yourself, it's pretty easy.
Assuming that your JSON String is in a variable called jsonString
let data = jsonString.dataUsingEncoding(NSUTF8StringEncoding)!
let json = try! NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)
Then you can access your data via subsript. For example if you want the Name of the second object in sgList
json["sgList"][1]["Name"]

You can do something like this using SwiftyJSON:
import SwiftyJSON
struct SqList {
let sqList: Array<SqElement>
init(json: JSON) {
let sqArray = json["sqList"].arrayValue.flatMap { SqElement(json: $0) }
self.sqList = sqArray
}
}
struct SqElement {
let id: String
let name: String
let price: String
let bigImagesUrl: String
let smallImagesUrl: String
init?(json: JSON) {
guard
let id = json["ID"].string,
let name = json["Name"].string,
let price = json["Price"].string,
let bigImagesUrl = json["BigImagesUrl"].string,
let smallImagesUrl = json["SmallImagesUrl"].string
else { return nil }
self.id = id
self.name = name
self.price = price
self.bigImagesUrl = bigImagesUrl
self.smallImagesUrl = smallImagesUrl
}
}
In code you just call:
let sqList = SqList(json: JSON(data: dataWithJSON))

Related

Swift Data Model from JSON Response

I am running into an issue building the correct data model for the following JSON response.
{
"resources": [
{
"courseid": 4803,
"color": "Blue",
"teeboxtype": "Championship",
"slope": 121,
"rating": 71.4
},
{
"courseid": 4803,
"color": "White",
"teeboxtype": "Men's",
"slope": 120,
"rating": 69.6
},
{
"courseid": 4803,
"color": "Red",
"teeboxtype": "Women's",
"slope": 118,
"rating": 71.2
}
]
}
Here is the current model. No matter what I do I can't seem to get the model populated. Here is also my URL session retrieving the data. I am new to Swift and SwiftUI so please be gentle. I am getting data back however I am missing something.
import Foundation
struct RatingsResources: Codable {
let golfcourserating : [GolfCourseRating]?
}
struct GolfCourseRating: Codable {
let id: UUID = UUID()
let courseID: Int?
let teeColor: String?
let teeboxtype: String?
let teeslope: Double?
let teerating: Double?
enum CodingKeysRatings: String, CodingKey {
case courseID = "courseid"
case teeColor = "color"
case teeboxtype
case teeslope = "slope"
case teerating = "rating"
}
}
func getCoureRating(courseID: String?) {
let semaphore = DispatchSemaphore (value: 0)
print("GETTING COURSE TEE RATINGS..........")
let urlString: String = "https://api.golfbert.com/v1/courses/\(courseID ?? "4800")/teeboxes"
print ("API STRING: \(urlString) ")
let url = URLComponents(string: urlString)!
let request = URLRequest(url: url.url!).signed
let task = URLSession.shared.dataTask(with: request) { data, response, error in
let decoder = JSONDecoder()
guard let data = data else {
print(String(describing: error))
semaphore.signal()
return
}
if let response = try? JSONDecoder().decode([RatingsResources].self, from: data) {
DispatchQueue.main.async {
self.ratingresources = response
}
return
}
print("*******Data String***********")
print(String(data: data, encoding: .utf8)!)
print("***************************")
let ratingsData: RatingsResources = try! decoder.decode(RatingsResources.self, from: data)
print("Resources count \(ratingsData.golfcourserating?.count)")
semaphore.signal()
}
task.resume()
semaphore.wait()
} //: END OF GET COURSE SCORECARD
First of all, never use try? while decoding your JSON. This will hide all errors from you. Use try and an appropriate do/catch block. In the catch block at least print the error.
Looking at your model there seem to be three issues here.
You don´t have an array of RatingsResources in your array. It is just a single instance.
let response = try JSONDecoder().decode(RatingsResources.self, from: data)
RatingsResources is not implemented correct.
let golfcourserating : [GolfCourseRating]?
should be:
let resources: [GolfCourseRating]?
Your coding keys are implemented wrong instead of:
enum CodingKeysRatings: String, CodingKey {
it should read:
enum CodingKeys: String, CodingKey {
You should add enum CodingKey with resources at struct RatingsResources
And decode:
if let response = try? JSONDecoder().decode(RatingsResources.self, from: data) {
// Your response handler
}

How to access varying JSON key in Swift 4.2 natively?

I have the following dummy JSON data for a bus to school.
{
"toSchool": {
"weekday": [{
"hour": 7,
"min": 10,
"type": null,
"rotary": false
}],
"sat": [{
"hour": 8,
"min": 15,
"type": null,
"rotary": true
}]
}
}
I would like to access "weekday" and "sat" key with a variable based on user input. How can I achieve this natively?
Using SwiftyJSON, it is fairly simple like below
let json = try JSON(data: data)
let userDirection = "shosfc"
let userWeek = "weekday"
let busList = json[userDirection][0][userWeek]
However, I was wondering how this would be done natively to remove dependencies.
It seems that CodingKey and enum might be the way to handle this. When the example is as simple as this, I can understand. However, I just cannot get my head around it for my particular usage where it involves custom objects not just String.
How can I do this? Please help.
This is based on your earlier question
func bus(isWeekday: Bool = true) -> [Bus] {
return isWeekday ? shosfc.weekDay : shosfc.sat
}
I think code that below will work:
struct SampleResponse: Codable {
let toSchool: ToSchool
}
struct ToSchool: Codable {
let weekday, sat: [Sat]
}
struct Sat: Codable {
let hour, min: Int
let type: String?
let rotary: Bool
}
To decode this type of response, you must decode this JSON with SampleResponse type.
let sampleResponse = try? newJSONDecoder().decode(SampleResponse.self, from: jsonData)
After that, you can reach variables like you asked.
You can convert JSON string to Dictionary in swift and access it the same way you just did:
func parseToDictionary(_ jsonStr: String) -> [String: Any]? {
if let data = jsonStr.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
let jsonStr = "{Your JSON String}"
let json = parseToDictionary(jsonStr)
let userDirection = "shosfc"
let userWeek = "weekday"
let busList = json[userDirection][0][userWeek]

How to parse JSON with custom parameters using Codable protocol

I have a JSON with keys
{
"yearOfManufacture":"20/9/2018",
"carSize":8,
"isNew":true,
"carAssets":[
{
"color":"5761807993001",
"nativeId":"{\"app\":\"1234/Car/Native_App\",\"web\":\" /8888/Car/Native_Car_Desktop\"}"
}
]
}
I am trying to parse using Codable protocol with struct models
struct Cars: Codable {
var yearOfManufacture: String?
var carSize: Int = 0
var isNew: Bool = true
var carAssets: [CarAssests]?
}
struct CarAssests: Codable {
var color: String?
var nativeId: String?
}
I am getting error like The data couldn’t be read because it isn’t in the correct format. I tried using CodingKeys with decoder container not getting the exact type of "nativeId": "{\"app\":\"1234/Car/Native_App\",\"web\":\" /8888/Car/Native_Car_Desktop\"}" not getting exact data type of this.
let decoder = JSONDecoder()
decoder.dataDecodingStrategy = .deferredToData
if let jsonData = jsonString.data(using: .utf8) {
do {
print(jsonData)
let assets = try decoder.decode(Cars.self, from: jsonData)
print(assets)
} catch {
print(error.localizedDescription)
}
}
I bet you are doing something like this:
let jsonString = """
{
"yearOfManufacture": "20/9/2018",
"carSize": 8,
"isNew": true,
"carAssets": [
{
"color": "5761807993001",
"nativeId": "{\"app\":\"1234/Car/Native_App\",\"web\":\" /8888/Car/Native_Car_Desktop\"}"
}
]
}
"""
In a multiline string, both \" and " mean the character ". So you have to write \\" to get the two characters \ and ":
let jsonString = """
{
"yearOfManufacture": "20/9/2018",
"carSize": 8,
"isNew": true,
"carAssets": [
{
"color": "5761807993001",
"nativeId": "{\\"app\\":\\"1234/Car/Native_App\\",\\"web\\":\\" /8888/Car/Native_Car_Desktop\\"}"
}
]
}
"""

How to insert JSON array model in Swift?

I have JSON like this but with more data:
[
{
"name": "Place 1",
"avatar": "https://sometext.it/image=1",
"id": "1",
"lng": 10.01,
"lat": 15.02
},
{
"name": "Place 2",
"avatar": "https://sometext.it/image=2",
"id": "2",
"lng": 15.02,
"lat": 15.03
}
]
I get JSON from URL and I want to insert them to array of places. I have class:
class Place {
var Avatar = ""
var Id = 0
var Lat = 0.0
var Lng = 0.0
var Name = ""
required init(avatar: String, id: Int, lat: Double, lng: Double, name: String) {
self.Avatar = avatar
self.Id = id
self.Lat = lat
self.Lng = lng
self.Name = name
}
}
And i create an Array:
var places: [Place] = []
I serialize JSON like this:
func parsingJson() {
guard let url = URL(string: "https://somelink.com") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
for result in json as! [[String:Any]] {
let avatar = result["avatar"] as! String
let id = result["id"] as! Int
let lat = result["lat"] as! Double
let lng = result["lng"] as! Double
let name = result["name"] as! String
let place = Place(avatar: avatar, id: id, lat: lat, lng: lng, name: name)
self.places.append(place)
print(result)
}
}catch {
print("JSON Error")
}
}
}.resume()
}
but that doesn't work, I have error like this: http://obrazki.elektroda.pl/9267167800_1497627255.png
I know that I have nil but I don't know why :( When I print the JSON when I'm serialising I see it on console.
The first, you are trying to get ID as an INT, when your JSON Object is showing that it's a string.. So you would need to do:
var id = Int()
if let someID = result["id"] as? String {
id = Int(someId)
} else {
print("ID failed as String")
}
However, i'd also recommend using a guard statement before your for loop:
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [[String:Any]] else {
print("Invalid json object type")
return
}
for result in json {
///your code.
}
you at least try to unwrap the variable, and get some Int instance to pass along
But obviously, the simpler solution would be to save the id field in your JSON to the type you would want on your front-end.. in this case a Number.
While it might not look as clean as your code, use unwraping methods like guards, and if-else logics as much as possible, and create failed unwrap fallbacks as much as possible as early on in your project. It helps in debugging down the line and creates a good base when your project becomes large enough.
Check up on Apple's guide and this here for some good starting points.
Late update, but re-reading on all this landed me on this neat article by Apple:
https://developer.apple.com/swift/blog/?id=37
You haven't nil, read error - Could not cast value of type NSTaggedPointerString to NSNumber This means that your id is String and you are casting it to Int so replace
let id = result["id"] as! Int
to
let id = result["id"] as! String

Get data from json array swift 2

I'm trying to get data drom the json array, this is the code that i'm trying, the thing is that i would like to get only the name that is inside this json
{
"tag": "getuser",
"success": 1,
"error": 0,
"uid": "56108b7e651ad2.95653404",
"user": {
"name": "2",
"phone": "2",
"email": "2"
}
}
I tryied this
let jsonData:NSDictionary = try NSJSONSerialization.JSONObjectWithData(urlData!, options:NSJSONReadingOptions.MutableContainers ) as! NSDictionary
let name = jsonData["user"]
print("Nombre del usuarioes: \(name)")
But this prints the whole user data, name, phone and email, how can i be able to print only the name or only the email?
You don't have to use a library and you don't have to use key-value coding.
The same way you're already using subscripting for your dictionary with this:
let name = jsonData["user"]
you just have to continue subscripting to find your value.
Example:
do {
let jsonData = try NSJSONSerialization.JSONObjectWithData(urlData!, options: []) as! NSDictionary
let user = jsonData["user"]!
let name = user["name"]
print(name)
} catch {
print(error)
}
Even better with safe unwrapping:
do {
if let data = urlData, let jsonData = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
if let user = jsonData["user"] as? NSDictionary, let name = user["name"] as? String {
print(name)
}
}
} catch {
print(error)
}
Note: in JSON, a dictionary is defined by {} and an array is defined by []. What you have here is a dictionary containing a dictionary, not an array (cf your question title).
A great library to decode json is SwiftyJSON
you can get sub-scripted data from the json like so
import SwiftyJSON
if let dataFromString = jsonString.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false) {
let json = JSON(data: dataFromString)
let name = json["user"]["name"].string
print(name)
}
Use your code, then get the field from jsonData by this:
let name = jsonData.valueForKeyPath("user.name") as! String
let email = jsonData.valueForKeyPath("user.email") as! String