iOS Swift JSON解析教程

时间:2020-02-23 14:45:55  来源:igfitidea点击:

在本教程中,我们将学习如何使用Swift在我们的iOS应用程序中解析JSON响应。
我们将在UITableView中显示已解析的响应。
要了解有关UITableView的更多信息,请先阅读本教程,然后再继续。

Swift JSON解析

JSON是最常用的从Web服务发送和接收数据的格式。
数据采用键值对的形式。
使用Swift字典,我们可以轻松地从键中获取值。

在本教程中,我们将解析本地资源文件中的JSON数据。

JSONSerialization类用于通过转换Data对象将JSON数据解析为键值对字典。

JSON数据的类型为[String:Any]

让我们创建一个单视图iOS应用程序,其中将本地创建的JSON文件中的数据解析到TableView中。

iOS JSON解析示例项目结构

在Main.storyboard中,我们向ViewController添加了标签和TableView。

我们已经将TableView委托设置为ViewController。

有关如何使用Storyboard设置TableView的更多信息,请参考本教程。

Swift JSON解析代码

我们将从文件中解析JSON数据。
response.json文件如下所示:

序列化数据

let url = Bundle.main.url(forResource: "response", withExtension: "json")
      
guard let jsonData = url else{return}
guard let data = try? Data(contentsOf: jsonData) else { return }
guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else{return}

在上面的代码中,我们通过Bundle.main.url实例获取文件。

Data实例将其转换为Data格式,然后将其序列化为JSON。

从JSON实例获取数据

现在我们有了JSON实例,我们可以通过以下方式获取数据:

if let dictionary = json as? [String: Any] {
          
          if let title = dictionary["title"] as? String {
           labelHeader.text = title
          }
          
          if let year = dictionary["year"] as? Double {
              print("Year is \(year)")
          }
          if let more_info = dictionary["more_info"] as? Double {
              //This doesn't get printed.
              print("More_info is \(more_info)")
          }
          
          for (key, value) in dictionary {
              print("Key is: \(key) and value is \(value)" )
          }
          
      }

在TableView中,我们将填充另一个JSON文件:

ViewController.swift

import UIKit

class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {

  @IBOutlet weak var labelHeader: UILabel!
  @IBOutlet weak var tableView: UITableView!
  

  var movieList = [MarvelData]()
  
  
  override func viewDidLoad() {
      super.viewDidLoad()
      //Do any additional setup after loading the view, typically from a nib.
      
      let url = Bundle.main.url(forResource: "response", withExtension: "json")
      
      guard let jsonData = url
          else{
              print("data not found")
              return
      }
      
      guard let data = try? Data(contentsOf: jsonData) else { return }
  
      guard let json = try? JSONSerialization.jsonObject(with: data, options: []) else{return}
      
      if let dictionary = json as? [String: Any] {
          
          if let title = dictionary["title"] as? String {
           labelHeader.text = title
          }
          
          if let year = dictionary["year"] as? Double {
              print("Year is \(year)")
          }
          if let more_info = dictionary["more_info"] as? Double {
              //This doesn't get printed.
              print("More_info is \(more_info)")
          }
          
          for (key, value) in dictionary {
              print("Key is: \(key) and value is \(value)" )
          }
          
      }
      
      //Now lets populate our TableView
      let newUrl = Bundle.main.url(forResource: "marvel", withExtension: "json")
      
      guard let j = newUrl
          else{
              print("data not found")
              return
      }
      
      guard let d = try? Data(contentsOf: j)
          else { print("failed")
              return
              
      }
      
      guard let rootJSON = try? JSONSerialization.jsonObject(with: d, options: [])
          else{ print("failedh")
              return
              
      }
      
      if let JSON = rootJSON as? [String: Any] {
          labelHeader.text = JSON["title"] as? String
          
          guard let jsonArray = JSON["movies"] as? [[String: Any]] else {
              return
          }
          
          print(jsonArray)
          let name = jsonArray[0]["name"] as? String
          print(name ?? "NA")
          print(jsonArray.last!["year"] as? Int ?? 1970)
          
          for json in jsonArray
          {
              guard let movieName = json["name"] as? String else{ return }
              guard let movieYear = json["year"] as? Int else{ return }
              movieList.append(MarvelData(movieName: movieName, movieYear: movieYear))
          }
          
          self.tableView.reloadData()
          
      }
           
  }

  override func didReceiveMemoryWarning() {
      super.didReceiveMemoryWarning()
      //Dispose of any resources that can be recreated.
  }
  
  func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return movieList.count
  }
  
  func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
      
  }

  func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
      let currentMovie = movieList[indexPath.row]
      let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as UITableViewCell
      cell.textLabel?.text = currentMovie.movieName
      cell.detailTextLabel?.text = "\(currentMovie.movieYear)"
      return cell
  }

}

struct MarvelData {
  
  let movieName: String
  let movieYear: Int
  
  public init(movieName: String, movieYear: Int) {
      self.movieName = movieName
      self.movieYear = movieYear
  }
}

在上面的代码中,我们还解析了第二个json文件,并将每个JSON Array元素追加到保存结构的数组中。

为了显示这些数据,我们必须在tableView实例上调用reloadData()

我们可以使在结构实例中设置json值的代码更好。

struct MarvelData {
    var movieName: String
    var movieYear: Int
 init(_ dictionary: [String: Any]) {
    self.movieName = dictionary["name"] as? String ?? "NA"
    self.movieYear = dictionary["year"] as? Int ?? 1970
  }
}

这样做我们可以更改:

guard let movieName = json["name"] as? String else{ return }
guard let movieYear = json["year"] as? Int else{ return }
movieList.append(MarvelData(movieName: movieName, movieYear: movieYear))

movieList.append(MarvelData(json))

无需使用for循环遍历jsonArray,我们可以轻松地在Swift中使用方便的运算符concatMap。
所以这:

for json in jsonArray
{
  movieList.append(MarvelData(json))
}

更改为

movieList = jsonArray.compactMap{return MarvelData(##代码##)}
//or
movieList = jsonArray.compactMap{MarvelData(##代码##)}
//or
movieList = jsonArray.compactMap(MarvelData.init)