JSON serialization with Swift 3 - json

I am trying to to populate a table by filling an array from JSON data. I am using the code below but keep getting the error:
Type 'Any' has no subscript members
on the following lines of code:
self.tableData.append(jsonResult[i]["title"]! as! String)
self.tableImages.append(jsonResult[i]["image"]! as! String)
self.tableDesc.append(jsonResult[i]["description"]! as! String)
self.tableValidity.append(jsonResult[i]["validity"]! as! String)
My code:
let str3 = Int(str2!)!
let url = NSURL(string: "https://www.*****.php")!
let task = URLSession.shared.dataTask(with: url as URL) { (data, response, error) -> Void in
if let urlContent = data {
do {
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: JSONSerialization.ReadingOptions.mutableContainers)
print(str3)
var i = 0
while i < str3 {
print(jsonResult[i]["title"]! as!String)
print(jsonResult[i]["image"]! as! String)
self.tableData.append(jsonResult[i]["title"]! as! String)
self.tableImages.append(jsonResult[i]["image"]! as! String)
self.tableDesc.append(jsonResult[i]["description"]! as! String)
self.tableValidity.append(jsonResult[i]["validity"]! as! String)
i = i + 1
}
} catch {
print("JSON serialization failed")
}
} else {
print("ERROR FOUND HERE")
}
DispatchQueue.main.async(execute: { () -> Void in
self.tableView.reloadData()
})
}
task.resume()

The compiler doesn't know the type of jsonResult, you have to tell what it is, for example with optional binding like this:
if let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options: []) as? [[String:AnyObject]] {
}
Here I downcast the JSON as an array of dictionaries. Use your loop inside this if let and it should work.

The compiler doesn't know the type the JSON object, you need to cast it to the actual type
let jsonResult = try JSONSerialization.jsonObject(with: urlContent, options:[]) as! [[String:Any]]
Mutable containers are not needed at all.
The C-while loop with incrementing a counter is very ugly. Don't use that in Swift. And you are using too many exclamation marks
for item in jsonResult {
print(item["title"] as! String)
print(item["image"] as! String)
self.tableData.append(item["title"] as! String)
self.tableImages.append(item["image"] as! String)
self.tableDesc.append(item["description"] as! String)
self.tableValidity.append(item["validity"] as! String)
}
PS: And multiple arrays as data source is also clumsy and error-prone. In an object oriented language a custom struct or class is preferable

Related

Type '[String:Any]' has no subscript members in Swift

I am trying to get information from a json result and append and access certain key values. However, I am getting the error "Type '[String:Any]' has no subscript members.
let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
for case let studentsInfo in json["results"] {
if let studentInfo = StudentResults(json: result) {
let name = firstName + " " + lastName
StudentsResults.append(name)
}
This is also the struct that I have placed in an extension.
struct StudentResults{
let firstName: String
let lastName: String
let latitude: Double
let longitude: Double
let mapString:String
let mediaURL: String
let objectID:String
let uniqueKey: String
let updatedAt: String
}
convenience init?(json: [String: Any]) {
guard let firstName = json["firstName"] as? String,
let lastName = json["lastName"] as? String
else {
return nil
}
return nil
This is also my GitHub page for this project if you would like to take a look at it
https://github.com/SteveBurgos95/UdacityMapProject
It appears to me that the problem is with this line of code:
let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: Any]
Whilst you are force casting it here as! [String: Any], the try? is giving you an optional value.
You have two options. You can be a crazy person and change try? to try!, or you can take the safer approach and wrap the code like so:
do {
guard let json = try JSONSerialization.jsonObject(with: data!, options: []) as [String: Any] else { return }
…
} catch {
// HANDLE ERROR
}

Error while parsing JSON in Swift, [String:Any] won't work

I'm trying to parse this JSON in Swift 3, but it crashes.
Here's the code
do{
let data1: Data = try! Data(contentsOf: NSURL(string: "https://gist.githubusercontent.com/DesWurstes/00baf946bd6d27e7e9355bd6e9969230/raw/a0de898faea8ddedb11b0db516967d0666255633/gist.json") as! URL)
let jsono = try JSONSerialization.jsonObject(with: data1, options: []) as! [String: Any]
}catch{
// catch isn't used here.
}
Here's the error I get when it crashes:
Could not cast value of type '__NSArrayI' (0x7fffe9cb9c08) to 'NSDictionary' (0x7fffe9cba158).
It crashes because not all of the elements of the array are string. (The root of the JSON is an array.)
To prevent it from crashing, changing the third line with this will be suitable:
let jsono = try JSONSerialization.jsonObject(with: data1, options: [])
But then, its type will be Any and I won't be able to parse it with
let string = jsono["something"] as! [String: Any] // Type "Any" has no subscript members
and this code won't run:
if let array = jsono as? [String: Any] {
print("test") // Doesn't print
}
While trying to fix error in the first code, I thought this code may work (Because it says can't convert Array to Dictionary):
let jsono = try JSONSerialization.jsonObject(with: data1, options: []) as! [String]
but it results...
Could not cast value of type '__NSDictionaryI' (0x7fffe9cba108) to 'NSString' (0x7fffea072f38).
Then how can I parse this JSON?
It looks like the JSON response returned from server is an array containing dictionaries of type [String: Any] so you can do:
if let array = jsono as? [[String: Any]] {
print("test") // Will print
for dictionary in array {
print(dictionary["url"] as! String)
}
}
Here you can download the playground I've written to test it out.
you have parse array response so you need to type cast json as? [[String: Any]]..
if your response is dictonary then you need to parse like json as? [String: Any]
func Callservice()
{
let jsonUrlString = "url"
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let courses = try JSONDecoder().decode([Course].self, from: data)
self.arrayData = courses
print(courses)
} catch let jsonErr {
print("Error serializing json:", jsonErr)
}
}.resume()
}
struct course:decodable{
let name:string?
}

Ambiguous use go 'subscript'

I am trying to get data that has been encoded as json in a php script.
My code:
func getJson(completion: #escaping (Array<CarPark>) -> Void) {
activityIndicatior.center = self.view.center
activityIndicatior.hidesWhenStopped = true
activityIndicatior.activityIndicatorViewStyle = UIActivityIndicatorViewStyle.gray
view.addSubview(activityIndicatior)
self.activityIndicatior.startAnimating()
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, options: JSONSerialization.ReadingOptions.mutableContainers) as AnyObject
for index in 0..<myJson.count {
if let entry = myJson[index] as? NSDictionary{
let name = entry["Name"] as! String
let longitude = Double(entry["Longitude"] as! String)
let latitude = Double(entry["Latitude"] as! String)
let quiet = Int(entry["Quiet"] as! String)
let moderate = Int(entry["Moderate"] as! String)
let busy = Int(entry["Busy"] as! String)
let coordinate = CLLocationCoordinate2D( latitude: latitude!, longitude: longitude!)
print("coordinate lat is : \(coordinate.latitude)")
print("coordinate long is : \(coordinate.longitude)")
print("coordinate full is: \(coordinate)")
let tempPark = CarPark(name: name, latitude: latitude!, longitude: longitude!, quiet: quiet!, moderate: moderate!, busy: busy!, coordinate: coordinate, level: "Nil")
let level = tempPark.calcAvailability()
tempPark.level = level
print("Availability is \(tempPark.level)")
self.tempCarParks.append(tempPark)
// print("amount of parks: \(self.carParks.count)")
print("name of parks in array: \(self.tempCarParks[index].name)")
print("Availability is \(tempPark.level)")
}
}
completion(self.tempCarParks)
}
catch
{
print("Error")
}
}
}
}
task.resume()
}
I am getting an error that says 'Ambiguous use of subscript' at the line:
if let entry = myJson[index] as? NSDictionary{
How can I fix this?
Since you know myJson is an array why do you cast the object to unspecified AnyObject?
That causes the error, the compiler needs to know the concrete types of all subscripted objects. Help the compiler then the compiler helps you:
let myJson = try JSONSerialization.jsonObject(with: content) as! [[String:Any]]
for entry in myJson { // don't use old-fashioned index based loops
let name = entry["Name"] as! String
...
.mutableContainers is completely useless in Swift.

Swift JSONSerialization.jsonObject Error

I've looked around but I don't find an answer to fix this error that has been bugging me. I tried adding a "as! NSMutableArray" but that gave me another error. Any ideas on how to fix it? I converted my project from Objective-C to Swift, so hopefully the code is all good I had 20+ errors now I'm down to 3 errors. Thank you.
Error Message:
'jsonObject' produces 'Any', not the expected contextual result type 'NSMutableArray'
Code for retrieving data from server
// Retrieving Data from Server
func retrieveData() {
let getDataURL = "http://ip/example.org/json.php"
let url: NSURL = NSURL(string: getDataURL)!
do {
let data: NSData = try NSData(contentsOf: url as URL)
jsonArray = JSONSerialization.jsonObject(with: data, options: nil)
}
catch {
print("Error: (data: contentsOf: url)")
}
// Setting up dataArray
var dataArray: NSMutableArray = []
// Looping through jsonArray
for i in 0..<jsonArray.count {
// Create Data Object
let dID: String = (jsonArray[i] as AnyObject).object(forKey: "id") as! String
let dName: String = (jsonArray[i] as AnyObject).object(forKey: "dataName") as! String
let dStatus1: String = (jsonArray[i] as AnyObject).object(forKey: "dataStatus1") as! String
let dStatus2: String = (jsonArray[i] as AnyObject).object(forKey: "dataStatus2") as! String
let dURL: String = (jsonArray[i] as AnyObject).object(forKey: "dataURL") as! String
// Add Data Objects to Data Array
dataArray.add(Data(dataName: dName, andDataStatus1: dStatus1, andDataStatus2: dStatus2, andDataURL: dURL, andDataID: dID))
}
self.myTableView.reloadData()
}
The jsonObject function will return a value of type Any but the jsonArray's type of NSMutableArray. And this function will throw an error if something is wrong, put a try keyword before it. In my experience, let change the type of jsonArray to array of dictionary, so you will extract data with ease.
do {
let data: Data = try Data(contentsOf: url as URL)
let jsonArray: [[String: AnyObject]] = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as! [[String: AnyObject]]
print("json: \(jsonArray)")
for dict in jsonArray {
let dataName = dict["dataName"] as! String
print("dataName: \(dataName)")
}
}
catch {
print("Error: (data: contentsOf: url)")
}

Strange behaviour of optionals in Swift 3

I have experienced a strange behaviour when parsing JSON data using Swift 3.
do {
let json = try JSONSerialization.jsonObject(with: data!, options: []) as! NSDictionary
let items:[AnyObject] = (json["items"] as? [AnyObject])!
for item in items {
let id:String = item["id"] as! String
print("ID: \(id)")
let info = item["volumeInfo"] as AnyObject
print(info)
let title = info["title"]
print(title)
}
} catch {
print("error thrown")
}
This produces the following output. Notice that info is an optional but if I try to unwrap it it states it is not an optional! The script crashes on let title = info["title"] As a result I can't access the title key. This behaviour has changed since Swift 2.
ID: lbvUD6LUyV8C
Optional({
publishedDate = 2002;
publisher = "Sams Publishing";
title = "Java Deployment with JNLP and WebStart";
})
You can do something like:
do {
let json = try JSONSerialization.jsonObject(with: data!) as! [String: Any]
let items = json["items"] as! [[String: Any]]
for item in items {
let id = item["id"] as! String
let info = item["volumeInfo"] as! [String: Any]
let title = info["title"] as! String
print(id)
print(info)
print(title)
}
} catch {
print("error thrown: \(error)")
}
I might suggest excising the code of the ! forced unwrapping (if the JSON was not in the form you expected, do you really want this to crash?), but hopefully this illustrates the basic idea.
The runtime type of info is Optional<Something>, but the compile time type (as you explicitly cast it) is AnyObject. How is the compiler supposed to know that the AnyObject will happen to be an Optional<Something> unless you tell it (in the form of a cast)?
Try:
let info = item["volumeInfo"] as AnyObject?
Now the compiler knows it's an Optional<AnyObject>, so you can unwrap it.
In Swift 3 the compiler must know the types of all subscripted objects if it's an array or dictionary. AnyObject – which has been changed to Any in Swift 3 – is not sufficient.
Since you know that the value for key volumeInfo is a dictionary cast it accordingly preferably using optional bindings
let info = item["volumeInfo"] as? [String:Any] {
print(info)
let title = info["title"] as! String
print(title)
}
This should do:
guard let json = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String: AnyObject],
let items = json["items"] as! Array<AnyObject>? else {
return
}
for item in items {
let id = item["id"] as! String
if let info = item["volumeInfo"] as? [String: AnyObject] {
let title = info["title"] as! String
} else {
// do something
}
}