Swift JSONSerialization as NSDictionary can't get to values inside - ios

JSON Results from output This image should help I am trying to get to [9] "vehicleLocation"
I have an API from UTA that tracks transit data in real-time and displays JSON data
let url = URL(string: "UTA API")!
let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
AlertController.showAlert(_inViewController: self, title: "Error", message: ("Fatal Error"))
} else {
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.allowFragments) as! NSDictionary
print(jsonResult)
//get to item within dictionary
let speed = jsonResult["speed"] as? [String:Any]
print(speed)
} catch {
AlertController.showAlert(_inViewController: self, title: "Error", message: "JSON Processing Failed")
}
}
}
}
task.resume()
this returns JSON data:
{
"serviceDelivery": {
"vehicleMonitoringDelivery": {
"vehicleActivity": [
{
"monitoredVehicleJourney": {
"extensions": {
"lastGPSFix": "2017-11-27T23:58:28.083",
"speed": 5.8690799999999994
},
I want the speed value and display in a label. But it comes up nil.
Any ideas and help would be welcomed.
Thanks

Looks like you're trying to access value with key speed at the first level, but it's actually way lower in the hierarchy, beyond a lot of children.

let dic = value[keyPath: "serviceDelivery.vehicleMonitoringDelivery"]
let array = dic["vehicleActivity"] as? NSArray
for speedValueDic in array{
let speed = speedValueDic["speed"] as? String
print(speed)
}

first try to access high level objects then when you reach speed cast it as string not as dictionary this will get you the value
let jsonString = "{\"device\":\"iPhone 6\",\"OS\":\"iOS 9\",\"name\":\"Apple\"}"
let data = jsonString.data(using: .utf8)
let json = try JSONSerialization.jsonObject(with: data!) as? [String : Any]
print (json!["name"] as! String)

var arrVehicleActivity = jsonResult["serviceDelivery"]?["vehicleMonitoringDelivery"]?["vehicleActivity"] as? [Any]
for i in 0..<arrVehicleActivity?.count {
var strSpeed = arrVehicleActivity?[i]?["monitoredVehicleJourney"]?["extensions"]?["speed"] as? String
print("Output should be speed :\(strSpeed)")
}
Hope will helpful to you!!
print("Output should be speed :(strSpeed)")
Output should be speed : 5.8690799999999994

Related

IOS/Swift: Parse JSON data and dictionary

I am trying to parse some nested JSON retrieved through an API but am having trouble isolating specific key-value pairs. In fact, I have some confusion over the difference between the JSON data and the dictionary obtained through serialization.
To retrieve the data I am using:
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
return
}
To convert the data to a JSON dictionary, I am doing
do {
let stringDic = try JSONSerialization.jsonObject(with: data, options: []) as? [String : Any]
} catch let error {
print(error)
}
When printed, this produces nested output of the form:
Optional(["document_tone": {
"tone_categories" = (
{
"category_id" = "emotion_tone";
"category_name" = "Emotion Tone";
and so forth
My question is how can I get a unique value such as that for the key category_name?
If I try to use
let myCat = stringDic["category_name"]
Fix-it requires let document_tone = stringDic?["document_tone"] which if printed to console just prints whole dictionary over again.
Thanks in advance for any suggestions.
It's pretty easy: () is array, {} is dictionary and the compiler must know the static types of all subscripted objects:
if let documentTone = stringDic?["document_tone"] as? [String:Any],
let toneCategories = documentTone["tone_categories"] as? [[String:Any]] {
for category in toneCategories {
print(category["category_name"])
}
}
I think it's better to use Decodable
struct Root:Decodable {
let documentTone : InnerItem
}
struct InnerItem:Decodable {
let toneCategories: [BottomItem]
}
struct BottomItem:Decodable {
let categoryId: String
let categoryName: String
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode(Root.self, from: data)
//print all names
result.documentTone.toneCategories.forEach {print($0.categoryName) }
} catch {
print(error)
}

How to append the data from the JSON

How to append the data ?
my api as:-
[
{
"Name": "Vegetables",
"PictureModel": {
"ImageUrl": "http://example.jpeg"
}
},
{
"Name": "Fruits",
"PictureModel": {
"ImageUrl": "http://example1.jpeg"
}
}
]
for fetching the data from json
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! Array<[String:Any]>
print(json)
let type = json[0]
let name = type["Name"] as! String
let PictureModel = type["PictureModel"] as! [String:Any]
print(PictureModel)
let nameofimage = PictureModel["ImageUrl"] as! String
print(nameofimage)
self.fetchedHome.append(Categories(name: name, images: nameofimage))
self.tableview.reloadData()
} catch {
print(error)
}
}
}.resume()
And i got the output in the tableview .In tableview i got first data as name-vegetable and image in UIImageview.
.But second data not display .So how to append ? how to give for loop and append
Use this code you will get one by one object in For loop:-
if let data = json as? NSArray {
for data in data {
if let data = data as? [String: AnyObject] {
//Here you will get data
print("Name", data["Name"])
}
}
}
Are you getting JSON correctly in print(json)?
If yes, then you should try looping on "json" as follows :-
for (var keyAttr in json) {
let dataRow = json[keyAttr]
}
Inside the dataRow you can get "Name" and "PictureModel".
I am hoping that it should work.
All the best.
let session = URLSession.shared
session.dataTask(with: request) { (data, response, error) in
if let response = response {
print(response)
}
if let data = data {
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! Array<[String:Any]>
if let data = json as? [String:Any] {
for data in data {
let name = data["Name"] as! String
let PictureModel = data["PictureModel"] as! [String:Any]
print(PictureModel)
let nameofimage = PictureModel["ImageUrl"] as! String
print(nameofimage)
self.fetchedHome.append(Categories(name: name, images: nameofimage))
}
}
self.tableview.reloadData()
} catch {
print(error)
}
}
}.resume()

Working with JSON data retrieving into Swift data types

I'm trying to get data from a URL. It was successful. I can download and convert to a dictionary[String : Any] but response is in nested loops. I don't to how to retrieve. Can someone suggest how to get text and value in the response?
func getDataFromUrl() {
let url = URL(string: "https://maps.googleapis.com/maps/api/distancematrix/json?units=imperial&departure_time=1408046331&origins=37.407585,-122.145287&destinations=37.482890,-122.150235")
let request = NSMutableURLRequest(url: url!)
let session = URLSession.shared
request.httpMethod = "GET"
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: {(data, response, error) in
do {
let jsonData = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any]
let destinationAddress = jsonData!["destination_addresses"]
print("Destination address \(String(describing: destinationAddress!))")
let origin_addresses = jsonData!["origin_addresses"]
print("Origin_addresses \(String(describing: origin_addresses!))")
let rows = jsonData!["rows"]
print("Rows \(String(describing: rows!))")
// Here I want to print text and value.
} catch {
// handle error
}
})
dataTask.resume()
}
The above answers work, but in my opinion the more swiftier approach is to use Codable.
class MyResponseType:Codable {
let destination_addresses:String
let rows:[MyCustomRowData]
}
class MyCustomRowData:Codable {
let elements:[MyCustomElementsData]
}
class MyCustomElementsData:Codable {
// properties here
}
Doing this, parsing the json is done like this:
let response = try? JSONDecoder().decode(MyResponseType.self, from: data)
Where the data variable is just the retrieved Data object from the request.
Initially you have to set up some boilerplate code to replicate your expected data format, but working with it is really worth it (and it makes it highly testable).
When the decode succeeds you have a perfectly typed object, it can also have optionals. It just wont decode if fields are missing or of the wrong type (which is a good thing).
Here is the way you can parse text and Value from response:
do{
if let jsonData = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers) as? [String: Any] {
if let destinationAddress = jsonData["destination_addresses"] as? [String] {
print(destinationAddress) //["1 Hacker Way, Menlo Park, CA 94025, USA"]
}
if let origin_addresses = jsonData["origin_addresses"] as? [String] {
print(origin_addresses) //["3251 Hillview Ave, Palo Alto, CA 94304, USA"]
}
if let rows = jsonData["rows"] as? [[String: AnyObject]] {
if rows.indices.contains(0) {
if let elements = rows[0]["elements"] as? [[String: AnyObject]] {
for element in elements {
if let duration = element["duration"] as? [String: AnyObject] {
let text = duration["text"] as? String ?? ""
print(text) //17 mins
let value = duration["value"] as? Int ?? 0
print(value) //1010
}
if let distance = element["distance"] as? [String: AnyObject] {
let text = distance["text"] as? String ?? ""
print(text) //7.2 mi
let value = distance["value"] as? Int ?? 0
print(value) //11555
}
}
}
}
}
}
}catch{ //error handle
}
Use this code:
let rows = jsonData["rows"] as! Array
let element = rows[0] as! Dictionary
let elementArray = element.value(forKey: "elements")
let distance = elementArray[0].value(forKey: "distance")
let text = distance.value(forKey: "text")
print(text)
let value = distance.value(forKey: "value")
print(value)

I tried to get the 'location' from the below JSON, but it returns response 'nil'

I tried to get the location from the below JSON, but it returns response nil, can you check it once. Below URL gives the response, but I want to display location from below JSON.
let url = URL(string: "http://beta.json-generator.com/api/json/get/4ytNy-Nv7")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print ("ERROR")
}
else
{
if let content = data
{
do
{
//Array
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
print(myJson)
let val = myJson["Location"] as? NSDictionary
print("val=\(val)")
}
catch
{
}
}
}
}
task.resume()
Don't use Foundation datatypes, such as NSDictionary, when they have native Swift counterparts. You also need to cast the JSON to an array of dictionaries. However, the problem that actually caused the issue was that Location is a String and not a dictionary.
guard let myJsonArray = try JSONSerialization.jsonObject(with: content) as? [[String:Any]], let myJson = myJsonArray.first else {return}
print(myJson)
let val = myJson["Location"] as? String
print("val=\(val)")
The root object of the JSON is clearly an array of a dictionary not something (AnyObject). The value for key Location is in the first object of the array
if let myJson = try JSONSerialization.jsonObject(with: content) as? [[String:Any]], !myJson.isEmpty { // check also that the array is not empty
print(myJson)
let val = myJson[0] // get first object of the array
let location = val["Location"] as? String ?? "n/a"
print("location = \(location)")
}
You can use the following function to download your data. Further more since your array has only one object, to access multiple locations you can iterate through the array objects
func downloadData(){
let url = URL(string: "http://beta.json-generator.com/api/json/get/4ytNy-Nv7")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print ("ERROR")
}
else
{
if let content = data
{
do
{
let myJson = try JSONSerialization.jsonObject(with: content) as? [[String:Any]]
let object = myJson?[0]
if let location = object?["Location"] as? String{
print(location)
}
}
catch
{
}
}
}
}
task.resume()
}
You can change this part :
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
Into :
let myJson = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as! [AnyHashable: Any]
Then if your JSON only have one object try this to get the Location :
let obj = myJson[0]
let location = obj["Location"] as? String
print("Location \(location)")

how to get the JSONArray from jsonObject in Swift 3.1

{
"status": true,
"status_code": 1,
"content": [
{
"cat_id": "3",
"cat_name": "Food",
"cat_parentid": "2"
},
{
"cat_id": "4",
"cat_name": "Entertainment",
"cat_parentid": "2"
},
{
"cat_id": "5",
"cat_name": "Cars",
"cat_parentid": "2"
},
{
"cat_id": "12",
"cat_name": "Personal Care",
"cat_parentid": "2"
}
],
"message": "Success"
}
UPDATE
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
completion((json as? AnyObject)!) //here completion callback will return the jsonObject to my UIViewController.
}
} catch let error {
print(error.localizedDescription)
}
this is my JSONObject. I am very new to the swift. how to get the content JSONArray and further process in swift.? Anybody can help me? Help will be appreciated.
This code checks if the status is true, gets the array for key content and prints all values in the array.
The array is clearly [[String:String]] so cast the object to this specific type.
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any] {
if let status = json["status"] as? Bool, status == true {
if let content = json["content"] as? [[String:String]] {
for category in content {
let id = category["cat_id"]
let name = category["cat_name"]
let parentId = category["cat_parentid"]
print(id , name, parentId)
}
}
}
}
} catch let error {
print(error.localizedDescription)
}
PS: As always, never use .mutableContainers in Swift. It's meaningless
Check whether your json has content array
if let content = json["content"] as? [Dictionary<String, AnyObject>] {
print(content) // it will give you content array
}
Get content array like this:
let allContent = json["content"] as? [[String: Any]]
Full sample:
do {
//create json object from data
if let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as? [String: Any] {
if let allContent = json["content"] as? [[String: Any]] {
for content in allContent {
let catId = content["cat_id"] as? String
let catName = content["cat_name"] as? String
let catParentId = content["cat_parentid"] as? String
print(">> catid=" + catId!)
print(">> catName=" + catName!)
print(">> catparentID=" + catParentId!)
}
}
}
} catch let error {
print(error.localizedDescription)
}
let content = dict.objectForKey("content")! as NSArray
Then you can get json of single object for parsing by
for var cat in content
{
print(cat)
}
Another alternative way, by using the library.
First, import JSON library for Swift - SwiftyJSON and use the code:
import SwiftyJSON
let json = JSON(<jsonObject data>)
let contentArray: Array<JSON> = json["content"].arrayValue
Library Integration
If you're using cocoapods then use this pod:
pod 'SwiftyJSON'
OR else just drag SwiftyJSON.swift to the project tree.
you can extract you data by providing key
if let array = result["content"] as? Array<AnyObject> {
print(arry)
}
You can access like below
if let filePath = Bundle.main.path(forResource: "sample", ofType: "json"), let data = FileManager().contents(atPath: filePath) {
do {
let dicRes = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any]
let contentArray = dicRes?["content"]
print("contentArray == \(contentArray)")
} catch {
}
}

Resources