如何使用Swift Decodable解码此JSON

This article is originally published at https://swiftsenpai.com on May 30, 2020.

本文最初于 2020年5月30日 发布在 https://swiftsenpai.com 上。

The Decodable protocol was introduced in Swift 4. Since then it has become the standard way for developers to decode JSON received from a remote server.

Swift 4中引入了Decodable协议。此后,它已成为开发人员解码从远程服务器接收的JSON的标准方法。

There are tons of tutorials out there that teach you how to utilize the Decodable protocol to decode various types of JSON structure. However, all these tutorials do not cover one specific type of JSON structure - JSON with dynamic keys.

那里有大量的教程,教您如何利用Decodable协议解码各种类型的JSON结构。 但是,所有这些教程都不涵盖一种特定类型的JSON结构- 具有动态键的JSON

What do I mean by JSON with dynamic keys? Take a look at the following sample JSON that shows a list of students:

带有动态键的JSON是什么意思? 看一下以下显示学生列表的示例JSON:

As you can see, the student ID is the key and the student information is the value.

如您所见,学生ID是密钥,而学生信息是值。

As iOS developers, what we usually need is an array of students, so that the list of students can be easily displayed on a table view. Therefore, aside from decoding the JSON, we also need to flatten the result (make student ID part of the student object) and transform it into an array.

作为iOS开发人员,我们通常需要的是一群学生,以便可以在表格视图中轻松显示学生列表。 因此,除了解码JSON之外,我们还需要将结果展平(使Student ID成为Student对象的一部分)并将其转换为数组。

When facing such JSON structure, some developers might fall back to the old decoding approach by using the JSONSerialization class and manually looping through and parsing each and every key-value pair.

当面对这样的JSON结构时,一些开发人员可能会通过使用JSONSerialization类并手动循环遍历和解析每个键值对来回到旧的解码方法。

However, I do not like the JSONSerialization way because it is more error-prone. On top of that, we will lose all the benefits that come along with using Decodable protocol.

但是,我不喜欢JSONSerialization方式,因为它更容易出错。 最重要的是,我们将失去使用Decodable协议带来的所有好处

In this article, I will walk you through the decoding approach that utilizes the Decodable protocol. After that, I will make the decoding logic generic so that it can be reused by some other object type.

在本文中,我将向您介绍利用Decodable协议的解码方法。 之后,我将使解码逻辑具有通用性,以便可以由其他一些对象类型重用。

With all that being said, let’s get right into it.

话虽这么说,让我们开始吧。

提取价值 (Extracting the Values)

As a recap, this is the JSON that we trying to decode:

作为回顾,这是我们尝试解码的JSON:

For simplicity sake, let’s focus on decoding the firstName and lastName for now. We will get back to the student ID later.

为了简单起见,让我们现在集中在解码firstNamelastName上。 稍后我们将返回到学生证。

First, let’s define a Student struct that conforms to the Decodable protocol.

首先,让我们定义一个符合Decodable协议的Student结构。

Next, we will need a Decodable struct that contains a Student array. We will use this struct to hold all the decoded Student objects. Let's call this struct DecodedArray.

接下来,我们将需要一个包含Student数组的Decodable结构。 我们将使用此结构来保存所有已解码的Student对象。 我们将此结构DecodedArray

In order to access the JSON’s dynamic keys, we must define a custom CodingKey struct. This custom CodingKey struct is needed when we want to create a decoding container from the JSONDecoder.

为了访问JSON的动态键,我们必须定义一个自定义的CodingKey结构。 当我们要从JSONDecoder创建解码容器时,需要此自定义CodingKey结构。

Note that we are only interested in the string value initializer because our keys are of type string, therefore we can just return nil in the integer value initializer.

注意,我们只对字符串值初始化程序感兴趣,因为我们的键是string类型的,因此我们可以在整数值初始化程序中返回nil。

With all that in place, we can now start implementing the DecodedArray initializer.

有了所有这些之后,我们现在就可以开始实现DecodedArray初始化程序了。

Let’s break down in detail what’s happening inside the initializer.

让我们详细分析初始化器中发生的事情。

  1. Create a decoding container using the DynamicCodingKeys struct. This container will contain all the JSON's first level dynamic keys.

    使用DynamicCodingKeys结构创建一个解码容器。 此容器将包含JSON的所有第一级动态键。

  2. Loop through each key to decode its respective Student object.

    遍历每个键以解码其各自的Student对象。

  3. Store all the decoded Student objects into the Student array.

    将所有已解码的Student对象存储到Student数组中。

That’s it for extracting firstName and lastName. Let's run all these in the Xcode playground to see them in action.

提取firstNamelastName 。 让我们在Xcode游乐场中运行所有这些命令,以查看它们的实际效果。

Here we convert the JSON string to data and ask the JSONDecoder to decode the JSON data as DecodedArray type. With that, we will be able to access all the decoded Student objects via DecodedArray.array.

在这里,我们将JSON字符串转换为数据,并要求JSONDecoder将JSON数据解码为DecodedArray类型。 这样,我们将能够通过DecodedArray.array访问所有已解码的Student对象。

Congratulations! You have successfully decoded all the Student objects. However, there are still works to do. In the next section, we will look into adding the student ID into the Student struct.

恭喜你! 您已经成功解码了所有Student对象。 但是,仍有工作要做。 在下一节中,我们将研究将学生ID添加到Student结构中。

提取密钥 (Extracting the Keys)

With what we currently have, adding the student ID into the Student struct is pretty straightforward. What we need to do is implement our own Student initializer and manually decode lastName, firstName and studentId.

根据目前的情况,将学生ID添加到Student结构中非常简单。 我们需要做的是实现我们自己的Student初始化程序,并手动解码lastNamefirstNamestudentId

Take a look at the following updated Student struct:

看一下以下更新的Student结构:

Let’s go through the changes we made on Student struct one by one:

让我们逐一查看对Student结构所做的更改:

  1. Define studentId to hold the extracted key (student ID).

    定义studentId以保存提取的键(学生ID)。

  2. Define coding keys that are needed for manual decoding.

    定义手动解码所需的编码密钥。
  3. Manually decode firstName and lastName.

    手动解码firstNamelastName

  4. This is where the magic happens. The decoding container codingPath is an array of CodingKey that contains the path of coding keys taken to get to this point in decoding. For our case, it should contain the key we obtained from DynamicCodingKeys in DecodedArray, which is the student ID.

    这就是魔术发生的地方。 解码容器codingPath是阵列CodingKey包含编码采取得到这一点在解码密钥的路径 对于我们的情况,它应包含我们从DecodedArray DynamicCodingKeys获得的密钥,即学生ID。

Let’s run this again in Xcode playground to see the final result.

让我们在Xcode操场上再次运行它以查看最终结果。

With that, we have successfully decoded and flattened a JSON with dynamic keys using the Decodable protocol. 🥳

这样,我们就可以使用Decodable协议使用动态密钥成功解码和展平JSON。 🥳

In the next section, let’s go one step further by improving the DecodedArray struct functionality and reusability.

在下一节中,我们将通过改善DecodedArray结构的功能性和可重用性来进一步DecodedArray

添加自定义集合支持 (Adding Custom Collection Support)

If you take a closer look into the DecodedArray struct, it is basically just a wrapper of the Student array. This makes it the perfect candidate to transform into a custom collection.

如果仔细研究DecodedArray结构,它基本上只是Student数组的包装。 这使其成为转换为自定义集合的理想人选。

By transforming into a custom collection, the DecodedArray struct can take advantage of the array literal, as well as all the standard collection functionalities such as filtering and mapping.

通过转换为自定义集合, DecodedArray结构可以利用数组文字以及所有标准集合功能(例如过滤和映射)的优势。

First, let’s define a typealias to represent the Student array and update the other part of DecodedArray accordingly. The typealias is required when we conform the DecodedArray to the Collection protocol later.

首先,我们定义一个类型typealias来表示Student数组,并相应地更新DecodedArray的另一部分。 该typealias当我们符合了要求DecodedArrayCollection协议后。

Here’s the updated DecodedArray where I have marked the changes made with ***.

这是更新的DecodedArray ,其中已标记了***所做的更改。

Next up, let’s extend the DecodedArray and conform to the Collection protocol.

接下来,让我们扩展DecodedArray并遵循Collection协议。

The details of conforming to the Collection protocol are beyond the scope of this article. If you want to know more, I highly recommend this great article.

符合Collection协议的详细信息不在本文的讨论范围之内。 如果您想了解更多,我强烈推荐这篇很棒的文章

That’s about it, we have fully transformed the DecodedArray struct into a custom collection.

就是这样,我们已经将DecodedArray结构完全转换为自定义集合。

Once again, let’s test out our changes in the Xcode playground. But this time with some cool functionalities that we gain from the Collection protocol conformance - array literal, map, and filter.

再次让我们在Xcode游乐场中测试我们的更改。 但是这一次我们可以从Collection协议一致性中获得一些很酷的功能-数组文字, mapfilter

Pretty cool isn’t it? With a little bit of extra effort, let’s make it even cooler by making the DecodedArray generic so that we can reuse it on other object types.

是不是很酷? 花费一些额外的精力,让我们通过使DecodedArray泛型使其更酷,以便我们可以在其他对象类型上重用它。

使其通用,提高可重用性 (Make It Generic, Increase Reusability)

To make our DecodedArray generic, we just need to add a generic parameter clause and replace all the Student type with a placeholder type T.

为了使我们的DecodedArray通用,我们只需要添加一个通用参数子句,并用占位符类型T替换所有Student类型。

Once again, I have marked all the changes with ***.

我再次用***标记了所有更改。

With all this in place, we can now use it to decode any object types. To see that in action, let’s use our generic DecodedArray to decode the following JSON.

完成所有这些操作后,我们现在可以使用它来解码任何对象类型。 为了看到实际效果,让我们使用通用的DecodedArray解码以下JSON。

The above JSON represents an array of Food objects grouped by category. Thus, we must first define the Food struct.

上面的JSON表示按类别分组的Food对象数组。 因此,我们必须首先定义Food结构。

After defining the Food struct, we are now ready to decode the given JSON using the generic DecodedArray.

定义Food结构之后,我们现在可以使用通用的DecodedArray解码给定的JSON。

Do note that decodedResult is an array of Food arrays ([[Food]]). Therefore, to get an array of Food objects ([Food]), we will apply flatmap on decodedResult to convert [[Food]] to [Food].

请注意, decodedResult是一个Food数组( [[Food]] )的数组。 因此,为了获得数组Food对象( [Food] ),我们将申请flatmapdecodedResult转换[[Food]][Food]

结语 (Wrapping Up)

This article only demonstrates decoding and flattening JSON with 2 layers, you can definitely apply the same concept on JSON with 3 or more layers. I’ll leave that as an exercise for you!

本文仅演示了解码和平坦化2层JSON,您肯定可以在3层或更多层的JSON上应用相同的概念。 我将其留给您练习!

If you would like to try out the decoding approach on Xcode playground, here’s the full sample code.

如果您想在Xcode操场上尝试解码方法,请参阅完整的示例代码

What do you think about this decoding approach? Feel free to leave your comments or thoughts in the comment section below.

您如何看待这种解码方式? 随时在下面的评论部分中留下您的评论或想法。

If you like this article, make sure to check out my other articles related to Swift.

如果您喜欢本文,请确保查看我与Swift相关的其他文章

You can also follow me on Twitter for more articles related to iOS development.

您也可以在Twitter上关注我,以获取有关iOS开发的更多文章。

Thanks for reading. 🧑🏻‍💻

谢谢阅读。 ‍💻

翻译自: https://medium.com/swlh/how-to-decode-this-json-using-swift-decodable-315464b0b1af

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值