Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions README.zh-cn.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
# JSONDecoder-Keypath

[![CircleCI](https://img.shields.io/circleci/project/github/0111b/JSONDecoder-Keypath.svg)](https://circleci.com/gh/0111b/JSONDecoder-Keypath) [![Isues](https://img.shields.io/github/issues/0111b/JSONDecoder-Keypath.svg)](https://github.com/0111b/JSONDecoder-Keypath/issues) ![License](https://img.shields.io/badge/license-MIT-ff69b4.svg) ![Swift Package Manager](https://img.shields.io/badge/spm-supported-blue.svg) ![Cocoapods](https://img.shields.io/badge/cocoapods-supported-blue.svg)

为 Foundation.JSONDecoder 添加嵌套键路径(key path)支持

<!-- TOC -->

- [设计初衷](#rationale)
- [用法](#usage)
- [实现原理](#under-the-hood)
- [安装](#installation)
- [Swift Package Manager](#swift-package-manager)
- [Cocoapods](#cocoapods)
- [总结](#conclusion)
- [计划与改进](#plans-and-improvements)

<!-- /TOC -->

### 设计初衷

在撰写本文时,我发现大多数流行的框架(主要是网络请求封装库)在处理键路径时采用了非常糟糕的方式。

假设我们有一个 `Decodable` (`Codable`) 对象,但在 API 响应中,该对象位于某个自定义路径下。大多数现有解决方案都提供了通过键路径提取对象的接口,并增加了 `Codable` 支持。但问题在于,在我见过的几乎所有案例中,其实现方式如下:
1. 使用 `JSONSerialization` 从 `Data` 中提取 `[String: Any]`
2. 在该字典中根据键路径进行查找
3. 将找到的对象重新转换为 `Data`
4. 使用 `JSONDecoder` 解析该对象

显而易见,这种数据的来回转换造成了额外的资源浪费。此外,在处理大量数据时,这种做法非常不推荐。

本软件包消除了前 3 个步骤。

### 用法

假设你有一个 `Item` 模型

```Swift
struct Item: Codable {
...
}
```

并且我们有如下 JSON:

```JSON
{
"foo" : <actual object>
}
```

要解析它,你需要编写:

```Swift
let jsonData: Data = ...
let item = try decoder.decode(Item.self, from: jsonData, keyPath: "foo")
```

同样支持嵌套键路径:

```Swift
let item = try decoder.decode(Item.self, from: jsonData, keyPath: "foo.bar")
```

键路径分隔符可以自定义:

```Swift
let item = try decoder.decode(Item.self, from: jsonData, keyPath: "foo/bar", keyPathSeparator: "/")
```

### 实现原理

本软件包为 `JSONDecoder` 添加了一个新方法:

```Swift
func decode<T>(_ type: T.Type,
from data: Data,
keyPath: String,
keyPathSeparator separator: String = ".") throws -> T where T : Decodable
```

在调用此方法时,`keypath` 被存储在 `JSONDecoder.userInfo` 中,随后调用标准的 `decode` 方法,并将私有类 `KeyPathWrapper<T>` 作为类型参数。在 `KeyPathWrapper` 的构造函数中,从 `userInfo` 获取键路径数据并据此遍历解码器。之后,再对原始类型进行解码。

### 安装

#### Swift Package Manager

将 `.Package(url: "https://github.com/0111b/JSONDecoder-Keypath.git")` 添加到你的 `Package.swift` 中。

#### Cocoapods

```Ruby
pod 'JSONDecoder-Keypath'
```

### 总结

基本上,这是一个关于自定义对象编码的简单实现,我只花了几个小时就完成了。但它表明,我们必须思考如何利用提供的 API,而不能懒于研究其底层实现。

### 计划与改进
- [ ] 扩展单元测试
- [x] 添加 cocoapods spec
- [ ] 添加 Carthage 支持