The JSON looks like this:
{
"00AK": {
"icao": "00AK",
"iata": "",
"name": "Lowell Field",
"city": "Anchor Point",
"country": "US",
"elevation": 450,
"lat": 59.94919968,
"lon": -151.695999146,
"tz": "America\/Anchorage"
},
"00AL": {
"icao": "00AL",
"iata": "",
"name": "Epps Airpark",
"city": "Harvest",
"country": "US",
"elevation": 820,
"lat": 34.8647994995,
"lon": -86.7703018188,
"tz": "America\/Chicago"
},
"00AZ": {
"icao": "00AZ",
"iata": "",
"name": "Cordes Airport",
"city": "Cordes",
"country": "US",
"elevation": 3810,
"lat": 34.3055992126,
"lon": -112.1650009155,
"tz": "America\/Phoenix"
}
}
As you can see the keys varies "00AK", "00AL", "00AZ", and so on. How do I parse this format of JSON?
let jsonData = //JSON DATA HERE
do {
let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as! NSDictionary
for (key, value) in dict {
let subDict = value as! NSDictionary
//Then you can access the values from subDict
} catch {
//ERROR HANDLING
}
So here it is I declare one structure as following
struct Model {
var iaco: String?
var iata: String?
var name: String?
var city: String?
var country: String?
var elevation: Int?
var lat: Double?
var lon: Double?
var tz: String? }
Then declare on array to hold the response result
var listOfModels = Array<Model>()
Then take a list of keys from response Dictionary and iterate over it to get result and store it in array
handleResponse { (response) in
for key in response.keys {
let dict = response[key] as? [String:Any]
var model = Model()
model.iaco = dict?["icao"] as? String
model.iata = dict?["iata"] as? String
model.name = dict?["name"] as? String
model.city = dict?["city"] as? String
model.country = dict?["country"] as? String
model.elevation = dict?["elevation"] as? Int
model.lat = dict?["lat"] as? Double
model.lon = dict?["lon"] as? Double
model.tz = dict?["tz"] as? String
listOfModels.append(model)
}
}
response.keys is used to get list of keys from dictionary.
You could try the below snippet:
func parseData() {
let jsonData = Data() /// your actual response data goes here...
do {
let dict = try JSONSerialization.jsonObject(with: jsonData, options: .allowFragments)
guard let swiftDict = dict as? [String : Any] else {
print("Not a valid response")
return
}
for (key, value) in swiftDict {
guard let valueDict = value as? [String: Any] else {
/// handle improper response here
return
}
/// Got the actual dictionary in 'valueDict'...
}
}
catch {
/// handle parsing error here
}
}
Related
I have the following JSON...
{
"id": "1000035148",
"petId": "3",
"ownerId": "1000",
"locationId": null,
"status": "Active",
“services”: [
{
"id": "5004",
“data”: 1,
“data1”: 0,
“data2": 63,
“data3": 0
}
]
}
And I'm only trying to return the following objects...
"id": "1000035148",
"petId": "3",
"ownerId": "1000",
"locationId": null,
"status": "Active"
How can I achieve this with the following code?
session.dataTask(with: request, completionHandler: { (data: Data?, response: URLResponse?, error: Error?) in
if let data = data {
do {
let jsonData = try JSONSerialization.jsonObject(with: data)
if let dictionary = jsonData as? [String: Any] {
if let nestedDictionary = dictionary["status"] as? [String: Any] {
for (key, value) in nestedDictionary {
print("Key: \(key), Value: \(value)")
}
}
}
print(jsonData)
} catch {
print("Error fetching data from API: \(error.localizedDescription)")
}
}
When I try to parse using the nestedDictionary = dictionary I get an error and it skips over the line. I'm confused on how to get just the key value pairs I want from the response.
Forget JSONSerialization and use Decodable with JSONDecoder:
struct DataModel: Decodable {
let id: String
let pedId: String?
let ownerId: String?
let locationId: String?
let status: String
}
do {
let dataModel = try JSONDecoder().decode(DataModel.self, from: data)
print("Status: \(dataModel.status)")
} catch ...
If you want to use JSONSerialization, note that status is not a dictionary, it's a String:
if let dictionary = jsonData as? [String: Any] {
if let status = dictionary["status"] as? String {
print("Status: \(status)")
}
}
There is an API that supplies JSON data that I would like to use. I've given a summary of the JSON below. At the top level, the key to each record is a unique ID that matches the ID in the record itself. These keys are integers in quotes (starting at 1, unsorted and probably not contiguous).
Reading the JSON isn't a problem. What is the Codable "Response" struct required to receive the data?
if let response = try? JSONDecoder().decode(Response.self, from: data)
The JSON
{
"2546": {
"id": "2546",
"title": "Divis and the Black Mountain"
},
"1": {
"id": "1",
"title": "A la Ronde"
},
"2": {
"id": "2",
"title": "Aberconwy House"
}
}
I had this once also, looks like whoever created this endpoint doesn't really understand how JSON works...
try this out and then just return response.values so you have a list of items
struct Item: Codable {
let id, title: String
}
typealias Response = [String: Item]
Use a more dynamic version of CodingKey. You can read more about it here: https://benscheirman.com/2017/06/swift-json/
Check the section "Dynamic Coding Keys"
The Codable type struct Response should be,
struct Response: Decodable {
let id: String
let title: String
}
Now, parse the json data using [String:Response] instead of just Response like so,
do {
let response = try JSONDecoder().decode([String:Response].self, from: data)
print(response) //["1": Response(id: "1", title: "A la Ronde"), "2546": Response(id: "2546", title: "Divis and the Black Mountain"), "2": Response(id: "2", title: "Aberconwy House")]
} catch {
print(error)
}
You should implement a custom CodingKey, something like that:
struct MyResponse {
struct MyResponseItemKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil }
static let id = MyResponseItemKey(stringValue: "id")!
static let title = MyResponseItemKey(stringValue: "title")!
}
struct MyResponseItem {
let id: String
let subItem: MyResponseSubItem
}
struct MyResponseSubItem {
let id: String
let title: String
}
let responseItems: [MyResponseItem]
}
Not sure if the key of each item and the value of id are always equal, that's why there are 2 IDs in MyResponse.
And, of course, MyResponse should conform to Codable:
extension MyResponse: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: MyResponseItemKey.self)
responseItems = try container.allKeys.map { key in
let containerForKey = try container.nestedContainer(keyedBy: MyResponseItemKey.self, forKey: key)
let id = try containerForKey.decode(String.self, forKey: .id)
let title = try containerForKey.decode(String.self, forKey: .title)
return MyResponseItem(id: key.stringValue, subItem: MyResponseSubItem(id: id, title: title))
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: MyResponseItemKey.self)
for responseItem in responseItems {
if let key = MyResponseItemKey(stringValue: responseItem.id) {
var subItemContainer = container.nestedContainer(keyedBy: MyResponseItemKey.self, forKey: key)
try subItemContainer.encode(responseItem.subItem.id, forKey: .id)
try subItemContainer.encode(responseItem.subItem.title, forKey: .title)
}
}
}
}
This is how you can use MyResponse:
let jsonString = """
{
"2546": {
"id": "2546",
"title": "Divis and the Black Mountain"
},
"1": {
"id": "1",
"title": "A la Ronde"
},
"2": {
"id": "2",
"title": "Aberconwy House"
}
}
"""
if let dataForJSON = jsonString.data(using: .utf8),
let jsonDecoded = try? JSONDecoder().decode(MyResponse.self, from: dataForJSON) {
print(jsonDecoded.responseItems.first ?? "")
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
if let dataFromJSON = try? encoder.encode(jsonDecoded) {
let jsonEncoded = String(data: dataFromJSON, encoding: .utf8)
print(jsonEncoded ?? "")
}
}
I need help to parsing the json using Swift . I could not parse them into an array of a class object using swift. What I tried is I bundle the json key value into an array of object and return. However, I could not deal with the types. It so confuse.
Could someone help me.
The json I have:
{
"mass_info1":
[{
"mass_id":7780,
"mass_date":"5/1/2019",
"mass_time":"7:30 PM",
"mass_location":"",
"mass_description": “Blah1”
} ] ,
"mass_info2":
[ {
"mass_id":7781,
"mass_date":"6/10/2019",
"mass_time":"7:30 PM",
"mass_location”:”1234 some place Los Angeles”,
"mass_description": “blah2”
} ] ,
"mass_info3":
[ {
"mass_id":7783,
"mass_date":"5/21/2019",
"mass_time":"7:30 PM",
"mass_location":"",
"mass_description": “blah3”
} ] ,
"mass_info4":
[ {
"mass_id":1115,
"mass_date":"4/5/2019",
"mass_time":"2:30 PM",
"mass_location":"4050 Mission Ave, Oceanside, CA 92057",
"mass_description": “blah4”
} ]
}
The class MassInfoObject import Foundation
struct MassInfoObject {
var mass_id:Int
var mass_date: String?
var mass_time:String?
var mass_location:String?
var mass_description:String?
init(mass_id:Int,mass_date:String,
mass_time:String,mass_location:String,mass_description:String)
{
self.mass_id=mass_id
self.mass_date=mass_date
self.mass_time=mass_time
self.mass_location=mass_location
self.mass_description=mass_description
}
}
and the code that I get the json from a URLSession.
func getjson()->[MassInfoObject]? {
let urlPath = "http://myclassinfo.com/dev/json.php"
let url = URL(string: urlPath)
var massObjArray = [MassInfoObject]()
var index:Int = 0
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
print("Task completed")
guard data != nil && error == nil else {
//print(error?.localizedDescription)
return
}
do {
if let jsonResult = try JSONSerialization.jsonObject(with: data!, options: []) as? NSDictionary {
for (key, value) in jsonResult {
//print("Value: \(value) for key: \(key)")
if let results = jsonResult[key] as? [String]
{
let dict = results[index].toJSON() as? [String:AnyObject] // can be any type here
for (key,value) in dict!{
let result2 = dict![key] as! String
print (result2)
}
}
}
}
} catch let parseError as NSError {
print("JSON Error \(parseError.localizedDescription)")
}
}
task.resume()
return massObjArray
}
The json you are trying to parse does not have a very logical structure. If you have control over the json, you should work on that first. If you don't have control I suggest converting the json on your client end before parsing.
The following works in a Playground:
let str = """
{ "mass_info1": [{ "mass_id":7780, "mass_date":"5/1/2019", "mass_time":"7:30 PM", "mass_location":"", "mass_description": "Blah1"
} ] ,
"mass_info2": [ { "mass_id":7781, "mass_date":"6/10/2019", "mass_time":"7:30 PM", "mass_location":"1234 some place Los Angeles", "mass_description": "Blah2"
} ] ,
"mass_info3":
[ { "mass_id":7783, "mass_date":"5/21/2019", "mass_time":"7:30 PM", "mass_location":"", "mass_description": "blah3"
} ] ,
"mass_info4":
[ { "mass_id":1115, "mass_date":"4/5/2019", "mass_time":"2:30 PM", "mass_location":"4050 Mission Ave, Oceanside, CA 92057", "mass_description": "blah4"
} ]
}
"""
struct MassInfoObject: Codable {
var mass_id: Int
var mass_date: String?
var mass_time: String?
var mass_location: String?
var mass_description: String?
}
let data = str.data(using: .utf8)!
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: [[String: Any]]]
let jsonArray = json!.values.map { $0 }
let cleanedJson = try JSONSerialization.data(withJSONObject: jsonArray, options: [])
let decoder = JSONDecoder()
let items = try decoder.decode([[MassInfoObject]].self, from: cleanedJson)
The first steps are to convert the json to something that can be parsed easily. Because now MassInfoObject conforms to Codable, you can use a simple JSONDecoder to decode an array of objects, this is just one line of code.
Also, in your networking code you are calling an async task, but your are returning immediately, that's not going to work. When you return from the getJson function, you don't have the results yet. The easiest thing to do is use a closure that you can call with the results:
In your networking code it would look as such:
func getjson(handler: #escaping ([MassInfoObject]?)->()) {
let urlPath = "http://myclassinfo.com/dev/json.php"
let url = URL(string: urlPath)
let task = URLSession.shared.dataTask(with: url!) { data, response, error in
print("Task completed")
guard let data = data && error == nil else {
//print(error?.localizedDescription)
return
}
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: [[String: Any]]]
let jsonArray = json!.values.map { $0 }
let cleanedJson = try JSONSerialization.data(withJSONObject: jsonArray, options: [])
let decoder = JSONDecoder()
let items = try decoder.decode([[MassInfoObject]].self, from: cleanedJson).compactMap { $0 }
handler(items)
} catch let parseError as NSError {
print("JSON Error \(parseError.localizedDescription)")
handler(nil)
}
}
task.resume()
}
If you have json that looks like this:
[
{
"mass_description": "Blah1",
"mass_date": "5/1/2019",
"mass_location": "",
"mass_time": "7:30 PM",
"mass_id": 7780
},
{
"mass_description": "blah2",
"mass_date": "6/10/2019",
"mass_location": "1234 some place Los Angeles",
"mass_time": "7:30 PM",
"mass_id": 7781
},
{
"mass_description": "blah3",
"mass_date": "5/21/2019",
"mass_location": "",
"mass_time": "7:30 PM",
"mass_id": 7783
},
{
"mass_description": "blah4",
"mass_date": "4/5/2019",
"mass_location": "4050 Mission Ave, Oceanside, CA 92057",
"mass_time": "2:30 PM",
"mass_id": 1115
}
]
You could easily decode this:
let items = try JSONDecoder().decode([MassInfoObject].self, data)
Thank Marcel, and everyone who helped me. Here is the code that I used
struct MassObj:Decodable{
var mass_id:Int
var mass_date: String
var mass_time:String
var mass_location:String
var mass_description:String
}
func getJsonArray2() {
let urlPath = "http://myclassinfo.com/dev/json.php"
guard let url = URL(string: urlPath) else {return }
let task = URLSession.shared.dataTask(with: url)
{
data, response, error in
print("Task completed")
guard data != nil && error == nil else {
//print(error?.localizedDescription)
return
}
do{
var massArray = [MassObj]()
massArray = try JSONDecoder().decode([MassObj].self, from:data!)
for object in massArray {
print("\(object.mass_id) == \(object.mass_date) == \(object.mass_description)")
}
}catch let jerr {
print(jerr)
}
}
task.resume()
}
================
and the json is
[
{
"mass_description": "Blah1",
"mass_date": "5/1/2019",
"mass_location": "",
"mass_time": "7:30 PM",
"mass_id": 7780
},
{
"mass_description": "blah2",
"mass_date": "6/10/2019",
"mass_location": "1234 some place Los Angeles",
"mass_time": "7:30 PM",
"mass_id": 7781
},
{
"mass_description": "blah3",
"mass_date": "5/21/2019",
"mass_location": "",
"mass_time": "7:30 PM",
"mass_id": 7783
},
{
"mass_description": "blah4",
"mass_date": "4/5/2019",
"mass_location": "4050 Mission Ave, Oceanside, CA 92057",
"mass_time": "2:30 PM",
"mass_id": 1115
}
]
I'm trying to create an array of dictionaries from JSON response.
Here is the code.
_ = postView.textView.rx.text
.subscribe(onNext: {[unowned self] _ in
let client = Alamofire.SessionManager.default
_ = client.request(Router.getFriends())
.rx_responseJSON()
.subscribe(onNext: { [weak self] data in
var names = [String]()
do {
let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], //'var' declarations with multiple variables cannot have explicit getters/setters
let friends = json["user"] as? [[String: Any]] {
for friend in friends {
if let name = friend["first_name"] as? String {
names.append(name)
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
print(names)
}, onError: { (error) -> Void in
debugPrint("Error: \(error)")
})
})
This is the error I'm getting
'var' declarations with multiple variables cannot have explicit
getters/setters
This is the JSON response,
{
"user": [
{
"id": 2,
"first_name": "Knysys",
"photo": "https://graph.facebook.com/437334666655912/picture/?type=large",
"last_seen_event": null,
"blocked": false
},
{
"id": 3,
"first_name": "ATester",
"photo": "https://graph.facebook.com/379988632393252/picture/?type=large",
"last_seen_event": 7,
"blocked": false
}
]
}
The desired output is this,,
var friends = [
[
"firstName": "SmartApps",
"photo": "https://graph.facebook.com/1248984075179327/picture/?type=large"
],
[
"firstName": "Knysys",
"photo": "https://graph.facebook.com/437334666655912/picture/?type=large"
],
[
"firstName": "ATester",
"photo": "https://graph.facebook.com/379988632393252/picture/?type=large"
]
]
Thanks in advance!
You forgot the if in the line let json = ...
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
let friends = json["user"] as? [[String: Any]] {
for friend in friends {
if let name = friend["first_name"] as? String {
names.append(name)
}
}
}
} catch {
print("Error deserializing JSON: \(error)")
}
Here is the code that worked,
let client = Alamofire.SessionManager.default
_ = client.request(Router.getFriends())
.rx_responseJSON()
.subscribe(onNext: { [weak self] data in
self?.friends.removeAll()
let json = data as? [String: Any]
let friends = json?["user"] as! [[String: Any]]
for i in 0 ..< friends.count{
let firstName: String = (friends[i]["first_name"] as! NSString) as String
let photo: String = (friends[i]["photo"] as! NSString) as String
let dict = [
"firstName" : firstName,
"photo" : photo
]
self?.friends.append(dict)
}
self?.friendTableView.reloadData()
self?.friendTableView.sizeToFit()
}, onError: { (error) -> Void in
debugPrint("Error retrieving friends: \(error)")
})
I have an object "itensList", it has the fields "name", "createdAt" and an array of "itens".
I want to be able to build JSON that looks like this:
{
"name": "List name"
"CreatedAt": "12:12 12/12/2016"
"itens": [
{
"title": "Item title"
"CreatedAt": "12:13 12/12/2016"
"isDone": false
},
{
"title": "Another item title"
"CreatedAt": "12:14 12/12/2016"
"isDone": true
}
]
}
I have tried a few different approaches with no success.
Item Object
class Item: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
dynamic var isDone = false
}
Item List Object
class ItemList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let itens = List<Item>()
}
For the example, let's make an object similar to what you must have:
class Iten {
let title:String
let createdAt:String
let isDone:Bool
init(title: String, createdAt: String, isDone: Bool) {
self.title = title
self.createdAt = createdAt
self.isDone = isDone
}
}
The trick I suggest is to add a computed value that will return a dictionary:
class Iten {
let title:String
let createdAt:String
let isDone:Bool
init(title: String, createdAt: String, isDone: Bool) {
self.title = title
self.createdAt = createdAt
self.isDone = isDone
}
var toDictionary: [String:AnyObject] {
return ["title": title, "createdAt": createdAt, "isDone": isDone]
}
}
Let's use it:
let iten1Dict = Iten(title: "title1", createdAt: "date1", isDone: false).toDictionary
let iten2Dict = Iten(title: "title2", createdAt: "date2", isDone: true).toDictionary
We now make the encapsulating dictionary:
let dict: [String:AnyObject] = ["name": "List name", "createdAt": "dateX", "itens": [iten1Dict, iten2Dict]]
To finish, we encode this dictionary to JSON data then we decode it as a String:
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(dict, options: .PrettyPrinted)
if let jsonString = String(data: jsonData, encoding: NSUTF8StringEncoding) {
print(jsonString)
}
} catch let error as NSError {
print(error)
}
And voilà:
{
"createdAt" : "dateX",
"itens" : [
{
"title" : "title1",
"createdAt" : "date1",
"isDone" : false
},
{
"title" : "title2",
"createdAt" : "date2",
"isDone" : true
}
],
"name" : "List name"
}
Raphael,
This piece of code builds a JSON query. It should get you started, just keep hacking and you'll find a way! That's the fun of programming!
func JSONquery()
let request = NSMutableURLRequest(URL: NSURL(string: "https://api.dropboxapi.com/2/files/get_metadata")!)
let session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
request.addValue("application/json",forHTTPHeaderField: "Content-Type")
request.addValue("path", forHTTPHeaderField: lePath)
let cursor:NSDictionary? = ["path":lePath]
do {
let jsonData = try NSJSONSerialization.dataWithJSONObject(cursor!, options: [])
request.HTTPBody = jsonData
print("json ",jsonData)
} catch {
print("snafoo alert")
}
let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
if let error = error {
completion(string: nil, error: error)
return
}
let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
//print("Body: \(strData)\n\n")
do {
let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers);
self.jsonPParser(jsonResult,field2file: "ignore")
/*for (key, value) in self.parsedJson {
print("key2 \(key) value2 \(value)")
}*/
completion(string: "", error: nil)
} catch {
completion(string: nil, error: error)
}
})
task.resume()
}
Like this:
var item = [
"title": "Item title",
"CreatedAt": "12:13 12/12/2016",
"isDone": false
]
var mainDictionary = [
"name": "List name",
"CreatedAt": "12:12 12/12/2016",
"items": [item]
]
And the just convert to json with NSJSONSerialization like this:
do {
let json = try NSJSONSerialization.dataWithJSONObject(mainDictionary, options: [])
} catch {
print(error)
}
UPDATE:
If you need to add values to array in dictionary you can do that like this:
if var items = mainDictionary["items"] as? NSMutableArray {
items.addObject(newItem)
mainDictionary["items"] = items
}