使用 Swift 解析 JSON
Posted
技术标签:
【中文标题】使用 Swift 解析 JSON【英文标题】:Parsing JSON using Swift 【发布时间】:2014-08-08 18:39:43 【问题描述】:我在一个自定义类中有一个异步方法,它返回一个包含字典的 NSArray。我正在尝试在 UITableView 中显示这些数据。一个问题是 getGenres 方法调用是异步的,因此数据会在表格显示后加载。主要问题是弄清楚如何实现这 3 个数据源方法。在 Objective C 中,这非常简单......
var genreList:NSArray?
override func viewDidLoad()
super.viewDidLoad()
Bookshop.getGenres
genres in
self.genreList = genres // copies data to the public NSArray
self.tableView.reloadData() // this returns the data async so need to reload the tableview
println("records: \(self.genreList?.count)") // displays the array of dictionaries correctly
// #pragma mark - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView!) -> Int
return 1
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
if self.genreList != nil
println("Rows in section: \(self.genreList?.objectAtIndex(0))")
return self.genreList?.count! // Value of optional 'Int?' not wrapped; did you mean to use '!'
return 3
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell?
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("GenreCell", forIndexPath: indexPath) as UITableViewCell
cell.textLabel.text = "Hello World!"
println("ROW: \(indexPath.row)")
println("data: \(self.genreList?.objectAtIndex(indexPath.row))")
var item = self.genreList?.objectAtIndex(indexPath.row) Variable 'item' inferred to have type 'AnyObject?', which may be unexpected.
//cell.textLabel.text = item.title
//var item:NSDictionary = self.genreList?.objectAtIndex(indexPath.row) as NSDictionary
//println("item: \(item)")
return cell
我做错了什么?完成一项非常简单的任务似乎太复杂了。
这是我的实用程序类:
import Foundation
class Bookshop
class func getGenres(completionHandler: (genres: NSArray) -> ())
//println("Hello inside getGenres")
let urlPath = "http://creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/list"
//println(urlPath)
let url: NSURL = NSURL(string: urlPath)
let session = NSURLSession.sharedSession()
var resultsArray:NSArray!
let task = session.dataTaskWithURL(url)
data, response, error in
//println("Task completed")
if(error)
println(error.localizedDescription)
//println("no error")
var err: NSError?
var options:NSJSONReadingOptions = NSJSONReadingOptions.MutableContainers
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: options, error: &err) as NSDictionary
if(err != nil)
println("JSON Error \(err!.localizedDescription)")
//NSLog("jsonResults %@", jsonResult)
let results: NSArray = jsonResult["genres"] as NSArray
//NSLog("jsonResults %@", results)
//resultsArray = results
//println("calling completion handler")
completionHandler(genres: results)
task.resume()
【问题讨论】:
您遇到了什么具体问题? (另外,我在您的问题中看不到任何关于 JSON 解析的内容 - 当您正在对其进行编辑以使其更易于回答时,您可能会考虑将标题更改为更具描述性的内容。) 我无法提取 NSArray self.genreList.count 中的索引数给了我可选“Int?”的错误值未包装;你的意思是用'!'并且行 var item = ... 给我一个警告变量“item”推断为“AnyObject?”类型,这可能是意外的。 【参考方案1】:swift 所做的只是消除了一堆我们真的不应该依赖的容易出错的功能,特别是依赖设置为零的消息总是容易出错。现在它有点冗长,但明显更明确,因此更安全。
objective-C 中return self.genreList.count
的等价物是:
override func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int
if let genres = self.genreList
return genres.count
else
// Note: this is the case when you haven't yet downloaded your genre
// list, do whatever is appropriate, this just shows an empty list
return 0
由于cellForRowAtIndexPath
只有在您的单元格计数不为零时才会被调用,因此该方法更简单,但实际上为了一致性/安全性,您遵循相同的一般流程:
override func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell?
if let genres = self.genreList as? [NSDictionary]
let cell:UITableViewCell = tableView.dequeueReusableCellWithIdentifier("GenreCell", forIndexPath: indexPath) as UITableViewCell
var item = genres[indexPath.row]
cell.textLabel.text = item["title"] as String
return cell
else
// Note: again, this is the case where you don't have your genre list
// list yet, so you can create whatever default cells needed.
return nil
【讨论】:
这里的基本原理很明确:当您真正需要的是检查已有的选项时,引入更多隐式展开的选项会使您的代码可读性降低且更容易出错。此外,您可以使用 beta 5 中引入的可选合并运算符来缩短第一种情况,将numberOfRowsInSection
正文减少为一行:return self.genreList?.count ?? 0
是的,合并运算符是一种更简洁的表达方式。在这种情况下,我更多的是试图建立一个如何有效和清晰地处理选项的一般模式。【参考方案2】:
将 self.genreList 的声明从 var genreList:NSArray?
更改为 var genreList:NSArray!
。然后您可以像在 Obj-C 中那样访问成员(没有问号)。也许,如果 getGenres 方法中的处理程序在另一个线程中,您需要编写
Bookshop.getGenres
genres in
self.genreList = genres // copies data to the public NSArray
dispath_asynch(dispatch_get_main_queue)
self.tableView.reloadData() // this returns the data async so need to reload the tableview
println("records: \(self.genreList.count)") // displays the array of dictionaries correctly
在你的 cellForRowAtIndexPath 中,你需要像这样转换项目:
var item = self.genreList[indexPath.row] as NSDictionary
dump(item) // just to show the dictionary in this scenario
cell.textLabel.text = item["title"] as String
另外:在 cellForRowAtIndexPath 中访问它之前,您必须检查 self.genreList != nil:
if genreList != nil
var item = self.genreList[indexPath.row] as NSDictionary
dump(item) // just to show the dictionary in this scenario
cell.textLabel.text = item["title"] as String
else
cell.textLabel.text = "Loading..."
【讨论】:
Ben,您的第一次更正很完美,但最后一点不起作用,因为每个索引都包含一个如下所示的字典: id = 9;记录 = 2; selfLink = "creative.coventry.ac.uk/~bookshop/v1.1/index.php/genre/id/9"; title = "食物和饮料"; 而不是作为 myGenre 对象的一个实例。我试图在适当的索引处检索字典,然后提取每个字符串和 int 以显示在表格视图单元格中。 第一行 var 项给出错误 'AnyObject' is not convertible to '[String : AnyObject]' 这很奇怪,使用 NSDictionary 怎么样?我还更改了代码看起来更酷:) 语法没有错误,但在该行崩溃并出现错误:致命错误:在展开可选值时意外发现 nil 那么在你的 getGenres 方法中,如果流派的类型到底是什么? (只需按住 alt 键点击)以上是关于使用 Swift 解析 JSON的主要内容,如果未能解决你的问题,请参考以下文章
需要帮助使用 Alamofire 使用 Swift 解析 JSON