I try to decode a json answer from a camera. But the camera is answering keys with null (this depends on the camera type).
The result string looks like that:
let string = "{\"id\":1,\"result\":[{\"names\":[\"getAvailableApiList\",\"getShootMode\",\"getSupportedShootMode\",\"getAvailableShootMode\",\"setFlashMode\",\"getFlashMode\",\"getSupportedFlashMode\",\"getAvailableFlashMode\",\"setSelfTimer\",\"getSelfTimer\",\"getSupportedSelfTimer\",\"getAvailableSelfTimer\",\"getSupportedMovieQuality\",\"startLiveview\",\"stopLiveview\",\"actTakePicture\",\"startMovieRec\",\"stopMovieRec\",\"awaitTakePicture\",\"getExposureMode\",\"getSupportedExposureMode\",\"getAvailableExposureMode\",\"setExposureCompensation\",\"getExposureCompensation\",\"getSupportedExposureCompensation\",\"getAvailableExposureCompensation\",\"setFNumber\",\"getFNumber\",\"getSupportedFNumber\",\"getAvailableFNumber\",\"setWhiteBalance\",\"getWhiteBalance\",\"getSupportedWhiteBalance\",\"getAvailableWhiteBalance\",\"getShutterSpeed\",\"getSupportedShutterSpeed\",\"getAvailableShutterSpeed\",\"setIsoSpeedRate\",\"getIsoSpeedRate\",\"getSupportedIsoSpeedRate\",\"getAvailableIsoSpeedRate\",\"actHalfPressShutter\",\"cancelHalfPressShutter\",\"getSupportedProgramShift\",\"getSupportedMovieFileFormat\",\"setContShootingMode\",\"getContShootingMode\",\"getSupportedContShootingMode\",\"getAvailableContShootingMode\",\"setWirelessFlashSetting\",\"getWirelessFlashSetting\",\"getSupportedWirelessFlashSetting\",\"getAvailableWirelessFlashSetting\",\"getApplicationInfo\",\"getEvent\",\"getTemporarilyUnavailableApiList\"],\"type\":\"availableApiList\"},{\"cameraStatus\":\"IDLE\",\"type\":\"cameraStatus\"},null,{\"liveviewStatus\":true,\"type\":\"liveviewStatus\"},null,[],[{\"continuousError\":\"Overheating Warning\",\"isContinued\":false,\"type\":\"continuousError\"},{\"continuousError\":\"Remote Control Not Currently Available\",\"isContinued\":false,\"type\":\"continuousdfError\"}],null,null,null,[],null,null,null,null,null,null,null,{\"currentExposureMode\":\"Aperture\",\"exposureModeCandidates\":[],\"type\":\"exposureMode\"},null,{\"currentSelfTimer\":0,\"selfTimerCandidates\":[0,2,5,10],\"type\":\"selfTimer\"},{\"currentShootMode\":\"still\",\"shootModeCandidates\":[\"still\",\"movie\",\"slow and quick\"],\"type\":\"shootMode\"},null,null,null,{\"currentExposureCompensation\":0,\"maxExposureCompensation\":15,\"minExposureCompensation\":-15,\"stepIndexOfExposureCompensation\":1,\"type\":\"exposureCompensation\"},{\"currentFlashMode\":\"on\",\"flashModeCandidates\":[\"on\",\"rearSync\",\"slowSync\"],\"type\":\"flashMode\"},{\"currentFNumber\":\"3.5\",\"fNumberCandidates\":[\"3.5\",\"4.0\",\"4.5\",\"5.0\",\"5.6\",\"6.3\",\"7.1\",\"8.0\",\"9.0\",\"10\",\"11\",\"13\",\"14\",\"16\",\"18\",\"20\",\"22\"],\"type\":\"fNumber\"},null,{\"currentIsoSpeedRate\":\"250\",\"isoSpeedRateCandidates\":[\"AUTO\",\"50\",\"64\",\"80\",\"100\",\"125\",\"160\",\"200\",\"250\",\"320\",\"400\",\"500\",\"640\",\"800\",\"1000\",\"1250\",\"1600\",\"2000\",\"2500\",\"3200\",\"4000\",\"5000\",\"6400\",\"8000\",\"10000\",\"12800\",\"16000\",\"20000\",\"25600\",\"32000\",\"40000\",\"51200\",\"64000\",\"80000\",\"102400\",\"128000\",\"160000\",\"204800\"],\"type\":\"isoSpeedRate\"},null,null,{\"currentShutterSpeed\":\"1/100\",\"shutterSpeedCandidates\":[],\"type\":\"shutterSpeed\"},{\"checkAvailability\":true,\"currentColorTemperature\":-1,\"currentWhiteBalanceMode\":\"Auto WB\",\"type\":\"whiteBalance\"},null,{\"focusStatus\":\"Not Focusing\",\"type\":\"focusStatus\"},null,null,{\"candidate\":[\"Single\",\"Continuous\"],\"contShootingMode\":\"Single\",\"type\":\"contShootingMode\"},null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null,null]}"
What is the best way to decode a json answer like that?
Use JSONSerialization and cast result to a dictionary of type [String: Any]
let data = string.data(using: .utf8)!
do {
let result = try JSONSerialization.jsonObject(with: data) as! [String: Any]
//extract info from result
if let array = result["result"] as? [Any] {
let item = array.filter( {
if let dict = $0 as? [String: Any] {
return dict.contains(where: { $0.key == "currentShootMode"})
}
return false
})
print(item)
}
} catch {
print(error)
}
to extract info from the item variable you need to cast again
if let mode = item.first as? [String: Any], let value = mode["currentShootMode"] {
print(value)
}
I'm very new to Swift and have spent several hours just trying to pull the photo_url key out of a JSON response.
I'm using this for the reading the JSON:
let jsonDictionary = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
Then:
if let eventsDictionary = jsonDictionary {
let upcomingEvents = UpcomingEvents(eventsDictionary: eventsDictionary)
completion(upcomingEvents)
} else {
completion(nil)
}
Here is my (failed) attempt to pull out the key:
init(eventsDictionary: [String : Any]) {
//photoUrl = eventsDictionary[EventKeys.photoUrl] as? String
let groups: NSArray = eventsDictionary["groups"] as! NSArray
let url: String = groups[0]
print("THIS IS YOUR RETURNED PHOTO URL--\(url)--END OF RETURNED PHOTO URL")
}
I changed "[String: Any]" to [String: AnyObject] and now i get this...
There are problems casting Any to NSArray. Just make your Init method taking [String:AnyObject]. But, better use Array instead of NSArray here
Try to get url use following code.
let firstObj = groups[0] as! [String: String] // use if let to unwrap is better
let url = firstObj["photo_url"]
To get "photo_url" from the json file in your photo,
it looks like this:
init(eventsDictionary: [String : Any]) {
if let groups = eventsDictionary["groups"] as? [NSDictionary]{
/*
// Get All URL
var urls : [String] = []
for group in groups{
if let url = group.value(forKey: "photo_url"){
urls.append(url)
}
}
*/
// Groups[0] url
let url: String = groups[0].value(forKey: "photo_url") as! String
print("THIS IS YOUR RETURNED PHOTO URL--\(url)--END OF RETURNED PHOTO URL")
}
}
You need to read json as `[String: Any].
if let eventsDictionary = json as? [String: Any] {
let upcomingEvents = UpcomingEvents(eventsDictionary: eventsDictionary)
completion(upcomingEvents)
}
Then, init your UpcomingEvents model like this
init(eventsDictionary: [String : Any]) {
let groups: NSArray = eventsDictionary["groups"] as! NSArray
let group1 = groups[0] as! NSDictionary
let photoURL = group1["photo_url"] as! String
print(photoURL)
}
Remote JSON parsing in Swift is new for me and I've spent weeks trying to figure this one out.
The JSON I'm pulling from is this guy:
http://www.odysseynewsmagazine.net/wp-json/wp/v2/posts?_embed
I'm trying to get to that "source_url" for an image for each post but it's nested within "media_details" which is nested within "wp:featuredmedia" which is nested within "_embedded" and I just keep getting errors.
The code I've written looks like this:
func parseData() {
fetchedSlug = []
//from odyssey site
let url = "http://www.odysseynewsmagazine.net/wp-json/wp/v2/posts?_embed"
var request = URLRequest(url: URL(string: url)!)
request.httpMethod = "GET"
let configuration = URLSessionConfiguration.default
let session = URLSession(configuration: configuration, delegate: nil, delegateQueue: OperationQueue.main)
let task = session.dataTask(with: request) { (data, response, error) in
if error != nil {
print("Error")
}
else {
do {
let fetchedData = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! NSArray
//Json objects to variables
for eachFetchedSlug in fetchedData {
let eachSlug = eachFetchedSlug as! [String: Any]
let slug = eachSlug["slug"] as! String
let link = eachSlug["link"] as! String
self.fetchedSlug.append(Slug(slug: slug, link: link))
}
self.slugTableView.reloadData()
}
catch {
print("Error2")
}
}
}
task.resume()
}
}//end of VC Class
class Slug {
//define variables
let slug: String?
let link: String?
init(slug: String?, link: String?) {
self.slug = slug
self.link = link
}
//creating dictionaries from Json objects
init(slugDictionary: [String : Any]) {
self.slug = slugDictionary["slug"] as? String
link = slugDictionary["link"] as? String
}
}
I'm also going to need the title of each post which is found in "rendered" within "title".
All of this info is populating labels within a reusable custom cell within a tableView. I can populate the slug and link labels, but not any of the nested info.
What's up with the underscore preceding "embedded"? Is that why I can't get to anything? Can I make it go away? I'm not allowed to download plugins or run custom scripts until I show them a working app.
Please check below code :
for eachFetchedSlug in fetchedData {
let eachSlug = eachFetchedSlug as! [String: Any]
let slug = eachSlug["slug"] as! String
let link = eachSlug["link"] as! String
self.fetchedSlug.append(Slug(slug: slug, link: link))
let title = eachSlug["title"] as! [String: Any]
let rendered = String(describing: title["rendered"])
print(rendered) // this is title
let embedded = eachSlug["_embedded"] as! [String: Any]
let wpfeaturedmedias = embedded["wp:featuredmedia"] as! [Any]
for wpfeaturedmedia in wpfeaturedmedias {
let featuredmedia = wpfeaturedmedia as! [String: Any]
let mediaDetails = featuredmedia["media_details"] as! [String: Any]
let mediaDetailsSize = mediaDetails["sizes"] as! [String: Any]
let mediaDetailsSizeThumbnail = mediaDetailsSize["thumbnail"] as! [String: Any] // getting only thumbnail. Based on you requirement change this to
let image = String(describing: mediaDetailsSizeThumbnail["source_url"])
print(image) // this is image
}
}
I added the code for only retrieving thumbnail. In the sizes so many types(medium,medium_large ...) are there. Based on your requirement, change the value.
Its better to add if let check for optionals. Because so many conversions are there. If it fails in any conversion, it will crash.
install Better REST API Featured Images plugin
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?
}