在 Swift 5 中使用 JSON 和 Codable

作者 | Simon Ng 
来源 | AppCoda

https://www.appcoda.com/json-codable-swift/

首先,什么是 JSON?JSON(JavaScript Object Notation 的缩写)是一种基于文本的、轻量的、用于存储和交换数据的方法。它通常用于表示客户机/服务器应用程序中的结构化数据及数据交换,是 XML 的替代方法。我们每天使用的许多 Web 服务都具有基于 JSON 的 API。大多数 iOS 应用程序,包括 Twitter,Facebook 和 Flickr,都以 JSON 格式将数据发送到后端 Web 服务。

例如,以下是以 JSON 来表示一个 Movie 对象:

{
    "title": "The Amazing Spider-man",
    "release_date": "03/07/2012",
    "director": "Marc Webb",
    "cast": [
        {
            "name": "Andrew Garfield",
            "character": "Peter Parker"
        },
        {
            "name": "Emma Stone",
            "character": "Gwen Stacy"
        },
        {
            "name": "Rhys Ifans",
            "character": "Dr. Curt Connors"
        }
    ]
}

如您所见,JSON 格式的数据比 XML 更具可读性,并且更易于解析。这里不会详细介绍 JSON,这不是本文的目的。如果您想了解有关该技术的更多信息,建议您访问 http://www.json.org/ 查阅 JSON 相关指南。

自 iOS 5 发布以来,iOS SDK 已经使开发人员可以轻松获取和解析 JSON 数据。它带有一个名为 NSJSONSerialization 的类,该类可以自动将 JSON 格式的数据转换为对象。在本章的后面,我将向您展示如何使用 API 解析由 Web 服务返回的一些 JSON 格式的数据。一旦了解了它的工作原理,就可以通过与其他 Web 服务集成来构建应用程序。

自 Swift 4 发布以来,Apple 引入了 Codable 协议以简化整个 JSON 归档和序列化过程。我们还将研究此新功能,并了解如何在 JSON 解析中应用它。

JSON Demo App

和往常一样,我们将创建一个 Demo 程序,我们称其为 KivaLoan。之所以将应用命名为 KivaLoan,是因为我们将利用 Kiva.org 提供的基于 JSON 的 API。Kiva 是一个非营利性组织,其目标是通过小额贷款将人们联系在一起。它允许个人借出低至 25 美元,以帮助在世界各地创造机会。Kiva 提供免费的基于 Web 的 API,供开发人员访问其数据。在我们的 Demo 中,我们将调用以下 Kiva API 来检索最新的贷款并将其显示在表格视图中:

https://api.kivaws.org/v1/loans/newest.json

上述 API 返回的数据为 JSON 格式。如下所示:

loans: (
        {
        activity = Retail;
        "basket_amount" = 0;
        "bonus_credit_eligibility" = 0;
        "borrower_count" = 1;
        description =         {
            languages =             (
                fr,
                en
            );
        };
        "funded_amount" = 0;
        id = 734117;
        image =         {
            id = 1641389;
            "template_id" = 1;
        };
        "lender_count" = 0;
        "loan_amount" = 750;
        location =         {
            country = Senegal;
            "country_code" = SN;
            geo =             {
                level = country;
                pairs = "14 -14";
                type = point;
            };
        };
        name = "Mar\U00e8me";
        "partner_id" = 108;
        "planned_expiration_date" = "2016-08-05T09:20:02Z";
        "posted_date" = "2016-07-06T09:20:02Z";
        sector = Retail;
        status = fundraising;
        use = "to buy fabric to resell";
    },
....
....
)

我们将学习如何使用 NSJSONSerialization 类将 JSON 格式的数据转换为对象。

为了使您专注于学习 JSON 实现,可以首先从 http://www.appcoda.com/resources/swift5/KivaLoanStarter.zip 下载项目模板。我已经为您创建了该应用程序的框架。这是一个简单的基于表的应用程序,显示了 Kiva.org 提供的贷款清单。该项目模板包括一个预先构建的 storyboard 和用于表视图控制器和原型单元的自定义类。如果您运行模板,它将生成一个空表应用程序。

创建 JSON 数据模型

我们将首先创建一个类来模拟贷款。加载 JSON 并非必需,但最佳做法是创建一个单独的类(或结构)来存储数据模型。Loan 类表示 KivaLoan 应用程序中的贷款信息,用于存储 Kiva.org 返回的贷款信息。为简单起见,我们不会使用所有返回的贷款数据。相反,该应用程序将仅显示贷款的以下字段:

贷款申请人姓名

name = "Mar\U00e8me";

贷款申请人所在的国家

location =         {
            country = Senegal;
            "country_code" = SN;
            geo =             {
                level = country;
                pairs = "14 -14";
                type = point;
            };
        };

贷款将如何使用

use = "to buy fabric to resell";

金额

"loan_amount" = 750;

这些字段足以填充表格视图中的 label 了。现在,使用 Swift File 模板创建一个新的类文件。将其命名为 Loan.swift 并声明如下的 Loan 结构:

struct Loan {
 
    var name: String = ""
    var country: String = ""
    var use: String = ""
    var amount: Int = 0
 
}

JSON 支持一些基本数据类型,包括数字,字符串,布尔值,数组和对象(具有键值对的关联数组)。

对于贷款的字段,贷款金额以数值形式存储在 JSON 格式的数据中。这就是为什么我们将 amount属性声明为 Int 类型的原因。其余字段声明为 String 类型。

使用 Kiva API 获取贷款

如前所述,Kiva API 是免费使用的,无需注册。您可以将浏览器指向以下网址,然后以 JSON 格式获取最新的筹款贷款。

https://api.kivaws.org/v1/loans/newest.json

让我们看看如何调用 Kiva API 并解析返回的数据。首先,打开 KivaLoanTableViewController.swift 并在开始时声明两个变量:

private let kivaLoanURL = "https://api.kivaws.org/v1/loans/newest.json"
private var loans = [Loan]()

我们仅定义了 Kiva API 的 URL,并声明了用于存储 Loan 对象数组的 loans 变量。接下来,在同一文件中插入以下方法:

func getLatestLoans() {
    guard let loanUrl = URL(string: kivaLoanURL) else {
        return
    }
 
    let request = URLRequest(url: loanUrl)
    let task = URLSession.shared.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
 
        if let error = error {
            print(error)
            return
        }
 
        // Parse JSON data
        if let data = data {
            self.loans = self.parseJsonData(data: data)
 
            // Reload table view
            OperationQueue.main.addOperation({
                self.tableView.reloadData()
            })
        }
    })
 
    task.resume()
}
 
func parseJsonData(data: Data) -> [Loan] {
 
    var loans = [Loan]()
 
    do {
        let jsonResult = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.mutableContainers) as? NSDictionary
 
        // Parse JSON data
        let jsonLoans = jsonResult?["loans"] as! [AnyObject]
        for jsonLoan in jsonLoans {
            let loan = Loan()
            loan.name = jsonLoan["name"] as! String
            loan.amount = jsonLoan["loan_amount"] as! Int
            loan.use = jsonLoan["use"] as! String
            let location = jsonLoan["location"] as! [String:AnyObject]
            loan.country = location["country"] as! String
            loans.append(loan)
        }
 
    } catch {
        print(error)
    }
 
    return loans
}

这两个方法构成了应用程序的核心部分。两个方法可以协同工作以调用 Kiva API,以 JSON 格式检索最新的贷款,并将 JSON 格式的数据转换为 Loan 对象数组。让我们来详细了解它们。

在 getLatestLoans 方法中,我们首先使用 Kiva Loan API 的 URL 实例化 URL 结构。初始化返回一个可选对象。这就是为什么我们使用 guard 关键字来查看可选对象是否具有值的原因。如果没有,我们简单返回并跳过方法中的所有代码。

接下来,我们使用 URL 创建 URLSession。URLSession 类提供用于通过 HTTP 和 HTTPS 处理在线内容的 API。共享 session 足以进行简单的 HTTP/HTTPS 请求。如果必须支持自己的网络协议,URLSession 还为您提供了创建自定义会话的选项。

URLSession 的一大优点是您可以添加一系列会话任务来处理数据的加载,以及上传和下载文件。

使用会话,您可以执行三种类型的任务:用于获取数据的数据任务(URLSessionDataTask),用于将文件下载到磁盘的下载任务(URLSessionDownloadTask)和用于从磁盘上载文件的上载任务(URLSessionUploadTask)。在这里,我们使用数据任务从 Kiva.org 检索内容。要将数据任务添加到会话中,我们使用特定的 URL 请求调用 dataTask 方法。添加任务后,会话将不执行任何操作。您必须调用resume方法(即task.resume())来启动数据任务。

像大多数网络 API 一样,URLSession API 是异步的。请求完成后,它将通过调用完成处理程序返回数据(以及错误)。

在完成处理程序中,返回数据后,我们立即检查错误。如果未发现错误,则调用 parseJsonData 方法。

返回的数据为 JSON 格式。我们创建了一个名为 parseJsonData 的辅助方法,用于将给定的 JSON 格式的数据转换为 Loan 对象的数组。Foundation 框架提供了 JSONSerialization 类,该类能够将 JSON 转换为 Foundation 对象并将 Foundation 对象转换为 JSON。在代码片段中,我们使用给定的 JSON 数据调用 jsonObject 方法以执行转换。

将 JSON 格式的数据转换为对象时,通常将顶层项目转换为 Dictionary 或 Array。在本文的情况下,Kiva API 返回的顶层数据将转换为字典。您可以使用键名 loans 访问贷款数组。

您如何知道要使用的键名呢?

您可以参考 API 文档或使用 JSON 浏览器(例如http://jsonviewer.stack.hu)测试 JSON 数据。如果您已将 Kiva API 加载到 JSON 浏览器中,则会显示以下结果:

{
  "paging": {
    "page": 1,
    "total": 5297,
    "page_size": 20,
    "pages": 265
  },
  "loans": [
    {
      "id": 794429,
      "name": "Joel",
      "description": {
        "languages": [
          "es",
          "en"
        ]
      },
      "status": "fundraising",
      "funded_amount": 0,
      "basket_amount": 0,
      "image": {
        "id": 1729143,
        "template_id": 1
      },
      "activity": "Home Appliances",
      "sector": "Personal Use",
      "use": "To buy home appliances.",
      "location": {
        "country_code": "PE",
        "country": "Peru",
        "town": "Ica",
        "geo": {
          "level": "country",
          "pairs": "-10 -76",
          "type": "point"
        }
      },
      "partner_id": 139,
      "posted_date": "2015-11-20T08:50:02Z",
      "planned_expiration_date": "2016-01-04T08:50:02Z",
      "loan_amount": 400,
      "borrower_count": 1,
      "lender_count": 0,
      "bonus_credit_eligibility": true,
      "tags": [
 
      ]
    },
    {
      "id": 797222,
      "name": "Lucy",
      "description": {
        "languages": [
          "en"
        ]
      },
      "status": "fundraising",
      "funded_amount": 0,
      "basket_amount": 0,
      "image": {
        "id": 1732818,
        "template_id": 1
      },
      "activity": "Farm Supplies",
      "sector": "Agriculture",
      "use": "To purchase a biogas system for clean cooking",
      "location": {
        "country_code": "KE",
        "country": "Kenya",
        "town": "Gatitu",
        "geo": {
          "level": "country",
          "pairs": "1 38",
          "type": "point"
        }
      },
      "partner_id": 436,
      "posted_date": "2016-11-20T08:50:02Z",
      "planned_expiration_date": "2016-01-04T08:50:02Z",
      "loan_amount": 800,
      "borrower_count": 1,
      "lender_count": 0,
      "bonus_credit_eligibility": false,
      "tags": [
 
      ]
    },
 
     ...

从上面的代码中可以看到,paging 和 loans 是两个顶层项。JSONSerialization 类转换 JSON 数据后,结果(即jsonResult)作为字典返回。这就是为什么我们可以使用键名 loans 来访问贷款数组的原因。如以下代码:

let jsonLoans = jsonResult?["loans"] as! [AnyObject]

有了 jsonLoans 数组后,我们就可以遍历该数组。每个数组项(即jsonLoan)都将转换为字典。在循环中,我们从每个词典中提取贷款数据,并将其保存在 Loan 对象中。同样,您可以通过研究 JSON 结果找到键(以黄色突出显示)。特定结果的值存储为 AnyObject。使用 AnyObject 是因为 JSON 值可以是 String,Double,Boolean,Array,Dictionary 或 null。这就是为什么您必须将值向下转换为特定类型(例如String和Int)的原因。最后,我们将 loan 对象放入 loans 数组,这是该方法的返回值。

for jsonLoan in jsonLoans {
    var loan = Loan()
 
    loan.name = jsonLoan["name"] as! String
    loan.amount = jsonLoan["loan_amount"] as! Int
    loan.use = jsonLoan["use"] as! String
    let location = jsonLoan["location"] as! [String: AnyObject]
    loan.country = location["country"] as! String
 
    loans.append(loan)
}

解析完 JSON 数据并返回贷款数组后,我们调用 reloadData 方法重新加载表。您可能想知道为什么我们需要调用 OperationQueue.main.addOperation 并在主线程中执行数据重新加载。

数据任务完成处理程序中的代码块在后台线程中执行。如果您在后台线程中调用 reloadData 方法,则不会立即重新加载数据。为了确保响应式 GUI 更新,应在主线程中执行此操作。这就是为什么我们调用 OperationQueue.main.addOperation 方法并请求在主队列中运行 reloadData 方法的原因。

OperationQueue.main.addOperation({
    self.tableView.reloadData()
})

在表格视图中显示贷款

放置好 loans 数组之后,我们要做的最后一件事就是在表视图中显示数据。在 KivaLoanTableViewController.swift 中更新以下方法:

override func numberOfSections(in tableView: UITableView) -> Int {
    // Return the number of ps
    return 1
}
 
override func tableView(_ tableView: UITableView, numberOfRowsInSection p: Int) -> Int {
    // Return the number of rows
    return loans.count
}
 
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath) as! KivaLoanTableViewCell
 
    // Configure the cell...
    cell.nameLabel.text = loans[indexPath.row].name
    cell.countryLabel.text = loans[indexPath.row].country
    cell.useLabel.text = loans[indexPath.row].use
    cell.amountLabel.text = "$\(loans[indexPath.row].amount)"
 
    return cell
}

如果您熟悉 UITableView 的实现,则上面的代码非常简单。在tableView(_:cellForRowAt:)方法中,我们从 loan 数组中检索贷款信息,并将其填充到自定义表格单元格中。需要注意的一件事是下面的代码:

"$\(loans[indexPath.row].amount)"

在某些情况下,您可能希望通过将字符串(例如$)和整数(例如loan [indexPath.row].amount)加在一起来创建一个字符串。Swift 提供了一种创建此类字符串的强大方法,称为字符串插值。您可以通过上述语法来使用它。

最后,将以下代码行插入到 viewDidLoad 方法中以获取贷款数据:

getLatestLoans()

编译并运行应用

现在该测试该应用了。编译并在模拟器中运行。启动后,该应用程序将从 Kiva.org 提取最新贷款,并将其显示在表格视图中。

Codable

自 Swift 4 发布以来,Apple 引入了一种使用 Codable 编码和解码 JSON 数据的新方法。我们将使用这种新方法重写 Demo 的 JSON 解码部分。

在我们进行修改之前,先来了解一下 Codable。如果您看过 Codable 的文档,会发现它只是协议组成的类型别名:

typealias Codable = Decodable & Encodable

Decodable 和 Encodable 是需要使用的两个实际协议。但是,为了方便起见,我们通常使用 Codable 别名来处理 JSON 编码和解码。

首先,与传统的 JSON 编码/解码方法相比,使用 Codable 有什么优势?如果返回上一部分并再次阅读代码,您会注意到我们必须手动解析 JSON 数据,将其转换为字典并创建 Loan 对象。

通过为开发人员提供不同的解码(或编码)JSON 方式,Codable 简化了整个过程。只要您的类型符合 Codable 协议,再加上新的 JSONDecoder,您就可以将 JSON 数据解码为指定的实例。下图说明了如何使用 JSONDecoder 将样本贷款数据解码为 Loan 实例。

解码 JSON

为了让您更好地了解 Codable 的工作原理,让我们创建一个 Playground 项目并编写一些代码。创建 Playground 项目后,声明以下 json 变量:

let json = """
{
    "name": "John Davis",
    "country": "Peru",
    "use": "to buy a new collection of clothes to stock her shop before the holidays.",
    "amount": 150
}
"""

我们将首先从基础开始。在这里,我们定义了一个非常简单的 JSON 数据,包含 4 个项目。前三个项目的值是 String 类型,而最后一个项目的类型是 Int。

接下来,声明 Loan 结构,如下所示:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
}

此 Loan 结构与上一节中定义的贷款结构非常相似,不同之处在于它采用了 Codable 协议。您还应该注意,属性名称与 JSON 数据的属性名称匹配。

现在,见证奇迹的时刻到了!

继续在您的 Playground 文件中插入以下代码:

let decoder = JSONDecoder()
 
if let jsonData = json.data(using: .utf8) {
 
    do {
        let loan = try decoder.decode(Loan.self, from: jsonData)
        print(loan)
 
    } catch {
        print(error)
    }
}

在上面的代码中,我们实例化 JSONDecoder 的实例,然后将我们先前定义的 JSON 字符串转换为 Data。魔术发生在以下代码行中:

let loan = try decoder.decode(Loan.self, from: jsonData)

您只需要使用 JSON 数据调用解码器的解码方法并指定要解码的值的类型(即Loan.self)即可。解码器将自动解析 JSON 数据并将其转换为 Loan 对象。

如果正确完成操作,则应该在控制台中看到以下行:

Loan(name: "John Davis", country: "Peru", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)

酷吧?

JSONDecoder 自动解码 JSON 数据,并将解码后的值存储在指定类型的相应属性(此处为 Loan)中。

使用自定义属性名称

之前,我向您展示了 JSON 解码的最简单示例。但是,解码过程并不总是那么简单。现在,让我们来看另一个例子。

有时,类型的属性名称和 JSON 数据的键不完全匹配。这时如何执行解码?

假设我们定义了 json 变量,如下所示:

let json = """
{
    "name": "John Davis",
    "country": "Peru",
    "use": "to buy a new collection of clothes to stock her shop before the holidays.",
    "loan_amount": 150
}
"""

在 JSON 数据中,贷款金额的键从 amount 更改为 loan_amount。如何在不更改 Loan 属性名称 amount 的情况下解码数据?

现在,像这样更新 Loan 结构:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
 
    enum CodingKeys: String, CodingKey {
        case name
        case country
        case use
        case amount = "loan_amount"
    }
}

要定义键和属性名称之间的映射,需要声明一个名为 CodingKeys 的枚举,该枚举的 RawValue 类型为 String 并符合 CodingKey 协议。在枚举中,您定义模型的所有属性名称及其在 JSON 数据中的相应键。如你所见,case amount 被定义为映射到关键的 loan_amount。如果属性名称和 JSON 数据的密钥相同,则可以省略赋值。

如果正确更改了代码,则应该能够在控制台中找到以下消息来解码更新的 JSON 数据:

Loan(name: "John Davis", country: "Peru", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)

使用嵌套的 JSON 对象

到目前为止,我们处理过的 JSON 数据只有一个层级。实际上,JSON数据通常有多个层级。现在,让我们看看如何解码嵌套的 JSON 对象。

首先,像这样更新 json 变量:

let json = """
{
    "name": "John Davis",
    "location": {
        "country": "Peru"
    },
    "use": "to buy a new collection of clothes to stock her shop before the holidays.",
    "loan_amount": 150
}
"""

通过引入具有嵌套 JSON 对象的 location 键值,我们对数据进行了一些小的的更改。我们如何解码这种 JSON 数据并从嵌套对象中检索 country 的值?

现在,如下修改 Loan 结构:

struct Loan: Codable {
    var name: String
    var country: String
    var use: String
    var amount: Int
 
    enum CodingKeys: String, CodingKey {
        case name
        case country = "location"
        case use
        case amount = "loan_amount"
    }
 
    enum LocationKeys: String, CodingKey {
        case country
    }
 
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
 
        name = try values.decode(String.self, forKey: .name)
 
        let location = try values.nestedContainer(keyedBy: LocationKeys.self, forKey: .country)
        country = try location.decode(String.self, forKey: .country)
 
        use = try values.decode(String.self, forKey: .use)
        amount = try values.decode(Int.self, forKey: .amount)
 
    }
}

与我们之前所做的类似,我们必须定义一个枚举 CodingKeys。对于 country,我们指定要映射到 location。要处理嵌套的 JSON 对象,我们需要定义一个附加的枚举。在上面的代码中,我们将其命名为 LocationKeys,并声明与嵌套对象的键 country 匹配的case country。

由于它不是直接映射,因此我们需要实现 Decodable 协议的初始化程序来处理所有属性的解码。在 init 方法中,我们首先使用 CodingKeys.self 调用解码器的 container 方法,以检索与指定编码键有关的数据,这些数据是 name、location、use 和 amount。

要解码特定值,我们使用特定键(例如.name)和关联的类型(例如String.self)调用解码方法。name、use 和 amount的解码非常简单。对于 country 属性,解码有点棘手。我们必须使用 LocationKeys.self 调用 nestedContainer 方法来检索嵌套的 JSON 对象。根据返回的值,我们进一步解码 country 的值。

这就是您使用嵌套对象解码 JSON 数据的方式。如果您正确地遵循了我的操作,则应该在控制台中看到以下消息:

Loan(name: "John Davis", country: "Peru", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)

处理数组

在从 Kiva API 返回的 JSON 数据中,通常附带不止一笔贷款。多个贷款以数组的形式构造。现在,让我们看看如何使用 Codable 解码 JSON 对象数组。

首先,像这样修改 json 变量:

let json = """
 
[
    {
        "name": "John Davis",
        "location": {
            "country": "Paraguay"
        },
        "use": "to buy a new collection of clothes to stock her shop before the holidays.",
        "loan_amount": 150
    },
    {
        "name": "Las Margaritas Group",
        "location": {
            "country": "Colombia"
        },
        "use": "to purchase coal in large quantities for resale.",
        "loan_amount": 200
    }
]
 
"""

要将上面的数组解码为 Loan 对象的数组,您需要修改以下代码行:

let loan = try decoder.decode(Loan.self, from: jsonData)

let loans = try decoder.decode([Loan].self, from: jsonData)

如您所见,解码JSON数据时只需指定 [Loan].self。

现在,JSON 数据已得到充分利用,但是有时您可能想忽略一些键/值对。假设我们像这样更新 json 变量:

let json = """
{
    "paging": {
        "page": 1,
        "total": 6083,
        "page_size": 20,
        "pages": 305
    },
    "loans": [
        {
            "name": "John Davis",
            "location": {
                "country": "Paraguay"
            },
            "use": "to buy a new collection of clothes to stock her shop before the holidays.",
            "loan_amount": 150
        },
        {
            "name": "Las Margaritas Group",
            "location": {
                "country": "Colombia"
            },
            "use": "to purchase coal in large quantities for resale.",
            "loan_amount": 200
        }
    ]
}
"""

该JSON数据带有两个顶层对象:paging 和 loans。显然,我们只对与贷款有关的数据感兴趣。在这种情况下,如何解码贷款数组?

为此,声明另一个名为 LoanDataStore 的结构,该结构也采用 Codable:

struct LoanDataStore: Codable {
    var loans: [Loan]
}

此 LoanDataStore 仅具有与 JSON 数据的 loans 匹配的 loans 属性。

现在,从中修改以下代码行:

let loans = try decoder.decode([Loan].self, from: jsonData)

let loanDataStore = try decoder.decode(LoanDataStore.self, from: jsonData)

解码器将自动解码 loans JSON 对象,并将其存储到 LoanDataStore 的借贷数组中。您可以添加以下代码行来验证数组的内容:

for loan in loanDataStore.loans {
    print(loan)
}

控制台应具有以下输出:

Loan(name: "John Davis", country: "Paraguay", use: "to buy a new collection of clothes to stock her shop before the holidays.", amount: 150)
Loan(name: "Las Margaritas Group", country: "Colombia", use: "to purchase coal in large quantities for resale.", amount: 200)

在 KivaLoan 应用程序中使用 Codable

现在,我相信您对如何使用 Codable 解码 JSON 有了一些想法,让我们回到 KivaLoan 项目并对其进行修改以使用 Codable。

打开 Loan.swift 并将其替换为以下代码:

import Foundation
 
struct Loan: Codable {
 
    var name: String = ""
    var country: String = ""
    var use: String = ""
    var amount: Int = 0
 
    enum CodingKeys: String, CodingKey {
        case name
        case country = "location"
        case use
        case amount = "loan_amount"
    }
 
    enum LocationKeys: String, CodingKey {
        case country
    }
 
    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
 
        name = try values.decode(String.self, forKey: .name)
 
        let location = try values.nestedContainer(keyedBy: LocationKeys.self, forKey: .country)
        country = try location.decode(String.self, forKey: .country)
 
        use = try values.decode(String.self, forKey: .use)
        amount = try values.decode(Int.self, forKey: .amount)
 
    }
}
 
 
struct LoanDataStore: Codable {
    var loans: [Loan]
}

上面的代码与我们之前开发的代码完全相同。LoanDataStore 旨在存储贷款数组。

接下来,用以下代码替换 KivaLoanTableViewController 类的 parseJsonData 方法:

func parseJsonData(data: Data) -> [Loan] {
 
    var loans = [Loan]()
 
    let decoder = JSONDecoder()
 
    do {
        let loanDataStore = try decoder.decode(LoanDataStore.self, from: data)
        loans = loanDataStore.loans
 
    } catch {
        print(error)
    }
 
    return loans
}

在这里,我们仅使用 JSONDecoder 而不是 JSONSerialization 来解码 JSON 数据。我们不再讨论代码,因为它与我们在 Playground 项目中所做的相同。

现在,您可以点击运行按钮,并在模拟器中测试该应用。一切都应该和以前一样。只是现在应用程序利用 Swift 5 中的 Codable 解码 JSON。

近期精彩内容推荐:  

 10个Redis使用技巧,很多人不知道的

 如何从Windows切换到Linux

 史上最污技术解读,我竟然秒懂了

 一文洞悉Python必备50种算法

在看点这里好文分享给更多人↓↓

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值