From bc8f5a4ad92027e700f3d77da2debe5dc33dd01f Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sat, 20 Feb 2021 10:40:18 +0800 Subject: [PATCH 01/54] Set theme jekyll-theme-hacker --- _config.yml | 1 + index.md | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 _config.yml create mode 100644 index.md diff --git a/_config.yml b/_config.yml new file mode 100644 index 0000000..fc24e7a --- /dev/null +++ b/_config.yml @@ -0,0 +1 @@ +theme: jekyll-theme-hacker \ No newline at end of file diff --git a/index.md b/index.md new file mode 100644 index 0000000..c99475f --- /dev/null +++ b/index.md @@ -0,0 +1,37 @@ +## Welcome to GitHub Pages + +You can use the [editor on GitHub](https://github.com/iwill/ExCodable/edit/gh-pages/index.md) to maintain and preview the content for your website in Markdown files. + +Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. + +### Markdown + +Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for + +```markdown +Syntax highlighted code block + +# Header 1 +## Header 2 +### Header 3 + +- Bulleted +- List + +1. Numbered +2. List + +**Bold** and _Italic_ and `Code` text + +[Link](url) and ![Image](src) +``` + +For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/). + +### Jekyll Themes + +Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/iwill/ExCodable/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file. + +### Support or Contact + +Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out. From 256338ec30c021f3ab36392a3d7c1137d9f9cbd5 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sat, 20 Feb 2021 11:05:17 +0800 Subject: [PATCH 02/54] Update index.md --- index.md | 110 ++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 89 insertions(+), 21 deletions(-) diff --git a/index.md b/index.md index c99475f..b22cf71 100644 --- a/index.md +++ b/index.md @@ -1,37 +1,105 @@ -## Welcome to GitHub Pages +[ExCodable](https://github.com/iwill/ExCodable) 是作者在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 -You can use the [editor on GitHub](https://github.com/iwill/ExCodable/edit/gh-pages/index.md) to maintain and preview the content for your website in Markdown files. +别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: +- 它是在 Swift Codable 基础上的扩展; +- 基于 `KeyPath` 读写属性,实现了 Key-Mapping; +- 差不多实现了 YYModel 的所有特性; +- 1 个文件、500 行代码。 -Whenever you commit to this repository, GitHub Pages will run [Jekyll](https://jekyllrb.com/) to rebuild the pages in your site, from the content in your Markdown files. +---- -### Markdown +近期我们团队准备开始使用 Swift,第一选择当然是站上巨人的肩膀。节前开始寻找一些开源框架,比如网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),毫无悬念。但是 JSON-Model 转换并没有找到一个合适的。 -Markdown is a lightweight and easy-to-use syntax for styling your writing. It includes conventions for +GitHub 上 Star 比较多的有几种类型: +- 刀耕火种型:这种框架用于读写 JSON,至于 Model 是不管的,比如 [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) —— 适用于少量使用、Model 很简单甚至没有的情况。 +- 优雅绅士型:Swift 内置的 Codable 是可以满足刚需的,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 +- 八仙过海型:[ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大好了,至少不够优雅。 -```markdown -Syntax highlighted code block +写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点。调研一番之后倾向于用 Codextended,因为能享受到官方的 Codable。 -# Header 1 -## Header 2 -### Header 3 +起初有考虑直接基于它做扩展来实现 Key-Mapping,但是后来发现受到限制较多,于是重新写了关键部分的实现,有些调整、也有些舍弃。 -- Bulleted -- List +Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,发现 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性。 -1. Numbered -2. List +主要特性: +- 通过 `KeyPath` 读写属性、`CodingKey` 读写 JSON,实现 Key-Mapping; +- 支持多个候选 Key; +- 支持 Key 嵌套; +- 支持自定义 Encode/Decode Handler; +- 支持使用 Subscript 进行 Encode/Decode; +- 支持类型自动转换以及自定义转换; +- 支持多种 Encoder/Decoder,默认使用 JSON,支持读写 `Data`、`String`、`Object` 类型 JSON 数据; +- 使用类型推断; +- 使用 `Optional` 类型取代抛出没什么用的错误; +- 支持 `struct`、`class`、`subclass`。 -**Bold** and _Italic_ and `Code` text +示例: -[Link](url) and ![Image](src) +定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; + +```swift +struct TestStruct: Equatable { + private(set) var int: Int = 0 + private(set) var string: String = "" +} +``` + +实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 `CodingKey` 的映射,`init` 和 `encode` 方法里只需一行代码; + +```swift +extension TestStruct: ExCodable { + + static var keyMapping: [KeyMap] = [ + KeyMap(\.int, to: "int", "i"), + KeyMap(\.string, to: "nested.string") + ] + + init(from decoder: Decoder) throws { + decode(with: Self.keyMapping, using: decoder) + } + func encode(to encoder: Encoder) throws { + encode(with: Self.keyMapping, using: encoder) + } + +} ``` -For more details see [GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/). +Encode、Decode 使用类型推断,使代码更具可读性; -### Jekyll Themes +```swift +let test = TestStruct(int: 100, string: "Continue") +let data = test.encoded() as Data? // Model -> JSON Data (or String, [String: Any]) +let copy1 = data?.decoded() as TestStruct? // JSON Data -> Model +let copy2 = TestStruct.decoded(from: data) // or Model <- JSON Data +XCTAssertEqual(copy1, test) +XCTAssertEqual(copy2, test) +``` + +更多示例可参考 [Usage](https://github.com/iwill/ExCodable#usage) 以及单元测试代码。 -Your Pages site will use the layout and styles from the Jekyll theme you have selected in your [repository settings](https://github.com/iwill/ExCodable/settings). The name of this theme is saved in the Jekyll `_config.yml` configuration file. +另外可以将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: -### Support or Contact +> Language: Swift +> Platform: All +> Completion: excodable +> Availability: Top Level + +```swift +<#extension/struct/class#> <#Type#>: ExCodable { + + static var <#keyMapping#>: [KeyMap<<#Type#>>] = [ + KeyMap(\.<#property#>, to: <#"key"#>), + <#...#> + ] + + init(from decoder: Decoder) throws { + decode(with: Self.<#keyMapping#>, using: decoder) + } + func encode(to encoder: Encoder) throws { + encode(with: Self.<#keyMapping#>, using: encoder) + } + +} +``` -Having trouble with Pages? Check out our [documentation](https://docs.github.com/categories/github-pages-basics/) or [contact support](https://support.github.com/contact) and we’ll help you sort it out. +在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 From 401077b2317cc4377dc3922e967cf21279f5f2b7 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sat, 20 Feb 2021 11:15:59 +0800 Subject: [PATCH 03/54] Update index.md --- index.md | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/index.md b/index.md index b22cf71..17bbfa0 100644 --- a/index.md +++ b/index.md @@ -1,21 +1,19 @@ -[ExCodable](https://github.com/iwill/ExCodable) 是作者在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 +[ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: - 它是在 Swift Codable 基础上的扩展; - 基于 `KeyPath` 读写属性,实现了 Key-Mapping; -- 差不多实现了 YYModel 的所有特性; +- 应该实现了所有常用的特性; - 1 个文件、500 行代码。 ----- - -近期我们团队准备开始使用 Swift,第一选择当然是站上巨人的肩膀。节前开始寻找一些开源框架,比如网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),毫无悬念。但是 JSON-Model 转换并没有找到一个合适的。 +我当然不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架,站上巨人的肩膀。比如网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 GitHub 上 Star 比较多的有几种类型: -- 刀耕火种型:这种框架用于读写 JSON,至于 Model 是不管的,比如 [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) —— 适用于少量使用、Model 很简单甚至没有的情况。 +- 刀耕火种型:这种框架用于读写 JSON,至于 Model 是不管的,比如 [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) —— 适用于少量使用 JSON 或者 Model 很简单的情况。 - 优雅绅士型:Swift 内置的 Codable 是可以满足刚需的,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 - 八仙过海型:[ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大好了,至少不够优雅。 -写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点。调研一番之后倾向于用 Codextended,因为能享受到官方的 Codable。 +写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点。调研一番之后倾向于用 Codextended,因为抱上官方 Codable 的大腿。 起初有考虑直接基于它做扩展来实现 Key-Mapping,但是后来发现受到限制较多,于是重新写了关键部分的实现,有些调整、也有些舍弃。 @@ -28,7 +26,7 @@ Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,发现 ` - 支持自定义 Encode/Decode Handler; - 支持使用 Subscript 进行 Encode/Decode; - 支持类型自动转换以及自定义转换; -- 支持多种 Encoder/Decoder,默认使用 JSON,支持读写 `Data`、`String`、`Object` 类型 JSON 数据; +- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON,支持读写 `Data`、`String`、`Object` 类型 JSON 数据; - 使用类型推断; - 使用 `Optional` 类型取代抛出没什么用的错误; - 支持 `struct`、`class`、`subclass`。 @@ -64,7 +62,7 @@ extension TestStruct: ExCodable { } ``` -Encode、Decode 使用类型推断,使代码更具可读性; +Encode、Decode 方法使用类型推断,使代码更具可读性; ```swift let test = TestStruct(int: 100, string: "Continue") From 6e9de4af17c00674f6a036789ea2273328f6c870 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sat, 20 Feb 2021 11:17:09 +0800 Subject: [PATCH 04/54] Update index.md --- index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.md b/index.md index 17bbfa0..6f167cd 100644 --- a/index.md +++ b/index.md @@ -77,10 +77,10 @@ XCTAssertEqual(copy2, test) 另外可以将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: -> Language: Swift -> Platform: All -> Completion: excodable -> Availability: Top Level +> Language: Swift +> Platform: All +> Completion: ExCodable +> Availability: Top Level ```swift <#extension/struct/class#> <#Type#>: ExCodable { From 8b5881a27a4bb97c0ac6911f9f4ae14124c1ec9f Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sun, 21 Feb 2021 09:28:00 +0800 Subject: [PATCH 05/54] Update index.md --- index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.md b/index.md index 6f167cd..955424c 100644 --- a/index.md +++ b/index.md @@ -6,11 +6,11 @@ - 应该实现了所有常用的特性; - 1 个文件、500 行代码。 -我当然不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架,站上巨人的肩膀。比如网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 +我当然不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架,准备站上巨人的肩膀。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 GitHub 上 Star 比较多的有几种类型: -- 刀耕火种型:这种框架用于读写 JSON,至于 Model 是不管的,比如 [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) —— 适用于少量使用 JSON 或者 Model 很简单的情况。 -- 优雅绅士型:Swift 内置的 Codable 是可以满足刚需的,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 +- 刀耕火种型:[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 仅用于读写 JSON,至于 Model 是不管的 —— 适用于少量使用 JSON 或者 Model 很简单的情况。 +- 优雅绅士型:Swift 内置的 Codable 可以满足刚需,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 - 八仙过海型:[ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大好了,至少不够优雅。 写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点。调研一番之后倾向于用 Codextended,因为抱上官方 Codable 的大腿。 From 114f7ba7b33df9c950d6e03409da5f74370c9d75 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sun, 21 Feb 2021 10:12:17 +0800 Subject: [PATCH 06/54] Update index.md --- index.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/index.md b/index.md index 955424c..e5a2872 100644 --- a/index.md +++ b/index.md @@ -2,34 +2,34 @@ 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: - 它是在 Swift Codable 基础上的扩展; -- 基于 `KeyPath` 读写属性,实现了 Key-Mapping; -- 应该实现了所有常用的特性; +- 基于 `KeyPath` 实现 Key-Mapping; +- 支持丰富的特性; - 1 个文件、500 行代码。 -我当然不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架,准备站上巨人的肩膀。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 +当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 GitHub 上 Star 比较多的有几种类型: -- 刀耕火种型:[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 仅用于读写 JSON,至于 Model 是不管的 —— 适用于少量使用 JSON 或者 Model 很简单的情况。 -- 优雅绅士型:Swift 内置的 Codable 可以满足刚需,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 -- 八仙过海型:[ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大好了,至少不够优雅。 +- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 仅用于读写 JSON,至于 Model 是不管的 —— 适用于少量使用 JSON 或者 Model 很简单的情况。 +- Swift 内置的 Codable 可以满足刚需,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 +- [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大好了,至少不够优雅。 写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点。调研一番之后倾向于用 Codextended,因为抱上官方 Codable 的大腿。 起初有考虑直接基于它做扩展来实现 Key-Mapping,但是后来发现受到限制较多,于是重新写了关键部分的实现,有些调整、也有些舍弃。 -Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,发现 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性。 +Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性。 主要特性: -- 通过 `KeyPath` 读写属性、`CodingKey` 读写 JSON,实现 Key-Mapping; +- 使用 `KeyPath` 映射到 `CodingKey`/`String` 类型的 JSON-Key,实现 Key-Mapping; - 支持多个候选 Key; - 支持 Key 嵌套; - 支持自定义 Encode/Decode Handler; - 支持使用 Subscript 进行 Encode/Decode; - 支持类型自动转换以及自定义转换; +- 支持 `struct`、`class`、`subclass`; - 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON,支持读写 `Data`、`String`、`Object` 类型 JSON 数据; - 使用类型推断; -- 使用 `Optional` 类型取代抛出没什么用的错误; -- 支持 `struct`、`class`、`subclass`。 +- 使用 `Optional` 类型取代抛出没什么用的错误,避免到处写 `try?`,有时还要套上括号。 示例: @@ -42,7 +42,7 @@ struct TestStruct: Equatable { } ``` -实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 `CodingKey` 的映射,`init` 和 `encode` 方法里只需一行代码; +实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 JSON-Key 的映射,`init` 和 `encode` 方法里只需一行代码; ```swift extension TestStruct: ExCodable { From 0ab94e2cb47e882ac1a636759edabac3e32e904a Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Sun, 21 Feb 2021 10:37:12 +0800 Subject: [PATCH 07/54] Update index.md --- index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/index.md b/index.md index e5a2872..e08d276 100644 --- a/index.md +++ b/index.md @@ -20,15 +20,15 @@ GitHub 上 Star 比较多的有几种类型: Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性。 主要特性: -- 使用 `KeyPath` 映射到 `CodingKey`/`String` 类型的 JSON-Key,实现 Key-Mapping; +- 使用 `KeyPath` 映射到 JSON-Key,实现 Key-Mapping; - 支持多个候选 Key; - 支持 Key 嵌套; - 支持自定义 Encode/Decode Handler; - 支持使用 Subscript 进行 Encode/Decode; - 支持类型自动转换以及自定义转换; -- 支持 `struct`、`class`、`subclass`; -- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON,支持读写 `Data`、`String`、`Object` 类型 JSON 数据; -- 使用类型推断; +- 支持 `struct`、`class`、subclass; +- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; +- 使用类型推断,支持 `Data`、`String`、`Object` 类型 JSON 数据; - 使用 `Optional` 类型取代抛出没什么用的错误,避免到处写 `try?`,有时还要套上括号。 示例: From 7210cb8d9cd9bedf61b4c0afb0b8d8b15675c023 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Wed, 3 Mar 2021 20:25:42 +0800 Subject: [PATCH 08/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index e08d276..44f2c77 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,7 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: -- 它是在 Swift Codable 基础上的扩展; +- 它是在 Swift Codable 基础上的扩展,可以与 Alamofire 无缝对接; - 基于 `KeyPath` 实现 Key-Mapping; - 支持丰富的特性; - 1 个文件、500 行代码。 From a11c062d6526960ddf6e5807a943e0a8c8fa6009 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Fri, 12 Mar 2021 17:26:08 +0800 Subject: [PATCH 09/54] Update index.md --- index.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/index.md b/index.md index 44f2c77..736c478 100644 --- a/index.md +++ b/index.md @@ -23,8 +23,7 @@ Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 ` - 使用 `KeyPath` 映射到 JSON-Key,实现 Key-Mapping; - 支持多个候选 Key; - 支持 Key 嵌套; -- 支持自定义 Encode/Decode Handler; -- 支持使用 Subscript 进行 Encode/Decode; +- 支持使用 Subscript 进行自定义 Encode/Decode; - 支持类型自动转换以及自定义转换; - 支持 `struct`、`class`、subclass; - 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; From 5f0a59e6335464f99dd4458436aeecb44f878b4b Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 10:47:40 +0800 Subject: [PATCH 10/54] Update index.md --- index.md | 61 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 30 insertions(+), 31 deletions(-) diff --git a/index.md b/index.md index 736c478..9829bca 100644 --- a/index.md +++ b/index.md @@ -1,63 +1,63 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: -- 它是在 Swift Codable 基础上的扩展,可以与 Alamofire 无缝对接; -- 基于 `KeyPath` 实现 Key-Mapping; -- 支持丰富的特性; -- 1 个文件、500 行代码。 +- 它是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 Alamofire 无缝对接; +- 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; +- 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); +- 轻量,1 个文件、不到 500 行代码。 当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 GitHub 上 Star 比较多的有几种类型: -- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 仅用于读写 JSON,至于 Model 是不管的 —— 适用于少量使用 JSON 或者 Model 很简单的情况。 -- Swift 内置的 Codable 可以满足刚需,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 相对简单的场景。 -- [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大好了,至少不够优雅。 +- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 仅用于读写 JSON,至于 Model 是不管的 —— 仅适用于没有 Model 或者 Model 很简单的情况; +- Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 比较简单的场景; +- [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大靠谱了,至少不够优雅。 -写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点。调研一番之后倾向于用 Codextended,因为抱上官方 Codable 的大腿。 +写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点意思。调研一番之后倾向于用 Codextended,因为抱上官方 `Codable` 的大腿。 -起初有考虑直接基于它做扩展来实现 Key-Mapping,但是后来发现受到限制较多,于是重新写了关键部分的实现,有些调整、也有些舍弃。 - -Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性。 +起初有考虑直接基于它做扩展来实现 Key-Mapping,但是后来发现受到限制较多,只能自己动手了。Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 主要特性: -- 使用 `KeyPath` 映射到 JSON-Key,实现 Key-Mapping; -- 支持多个候选 Key; -- 支持 Key 嵌套; +- 使用 `KeyPath` 映射到 Coding-Key,实现 Key-Mapping; +- 支持多个候选 Coding-Key; +- 支持 Coding-Key 嵌套; - 支持使用 Subscript 进行自定义 Encode/Decode; -- 支持类型自动转换以及自定义转换; +- 支持数据类型自动转换以及自定义转换; - 支持 `struct`、`class`、subclass; - 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; -- 使用类型推断,支持 `Data`、`String`、`Object` 类型 JSON 数据; -- 使用 `Optional` 类型取代抛出没什么用的错误,避免到处写 `try?`,有时还要套上括号。 +- 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; +- 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号。 + +> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段也抛出错误,导致整个解析都出错就不好了。没错,这确实可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以我认为,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就崩给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据。 -示例: +抱歉废话有点多,代码上晚了: 定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; +> `Optional` 类型不需要默认值; +> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/iwill/ExCodable#usage); + ```swift struct TestStruct: Equatable { private(set) var int: Int = 0 - private(set) var string: String = "" + private(set) var string: String? } ``` -实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 JSON-Key 的映射,`init` 和 `encode` 方法里只需一行代码; +实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 Coding-Key 的映射,`init` 和 `encode` 方法都只要一行代码; ```swift extension TestStruct: ExCodable { - static var keyMapping: [KeyMap] = [ KeyMap(\.int, to: "int", "i"), KeyMap(\.string, to: "nested.string") ] - init(from decoder: Decoder) throws { decode(with: Self.keyMapping, using: decoder) } func encode(to encoder: Encoder) throws { encode(with: Self.keyMapping, using: encoder) } - } ``` @@ -65,9 +65,9 @@ Encode、Decode 方法使用类型推断,使代码更具可读性; ```swift let test = TestStruct(int: 100, string: "Continue") -let data = test.encoded() as Data? // Model -> JSON Data (or String, [String: Any]) -let copy1 = data?.decoded() as TestStruct? // JSON Data -> Model -let copy2 = TestStruct.decoded(from: data) // or Model <- JSON Data +let data = test.encoded() as Data? // Model encode to JSON +let copy1 = data?.decoded() as TestStruct? // decode JSON to Model +let copy2 = TestStruct.decoded(from: data) // or Model decode from JSON XCTAssertEqual(copy1, test) XCTAssertEqual(copy2, test) ``` @@ -83,20 +83,19 @@ XCTAssertEqual(copy2, test) ```swift <#extension/struct/class#> <#Type#>: ExCodable { - - static var <#keyMapping#>: [KeyMap<<#Type#>>] = [ + static var <#keyMapping#>: [KeyMap<<#SelfType#>>] = [ KeyMap(\.<#property#>, to: <#"key"#>), <#...#> ] - init(from decoder: Decoder) throws { - decode(with: Self.<#keyMapping#>, using: decoder) + decode<#Reference#>(with: Self.<#keyMapping#>, using: decoder) } func encode(to encoder: Encoder) throws { encode(with: Self.<#keyMapping#>, using: encoder) } - } ``` +## 感谢 + 在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 From 7e84b59d922a63ce1ee38617847f81812ac7595a Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 10:53:27 +0800 Subject: [PATCH 11/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 9829bca..d760c25 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,7 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: -- 它是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 Alamofire 无缝对接; +- 它是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 Alamofire 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); - 轻量,1 个文件、不到 500 行代码。 From 3c5fa274c0f7931b969e9f1cd3b8797486fb1460 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 10:53:56 +0800 Subject: [PATCH 12/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index d760c25..bd5b1f6 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,7 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: -- 它是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 Alamofire 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); +- 它是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); - 轻量,1 个文件、不到 500 行代码。 From 0f240d73abe3140890d84bfc251b85ace53bb414 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 10:56:40 +0800 Subject: [PATCH 13/54] Update index.md --- index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.md b/index.md index bd5b1f6..84b0400 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,7 @@ -[ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 +[`ExCodable`](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 -别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是 ExCodable 真的有点不一样: -- 它是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); +别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: +- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); - 轻量,1 个文件、不到 500 行代码。 From e038b0484a48b2d8a37f98dd9c6f791e371527c1 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 10:57:35 +0800 Subject: [PATCH 14/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 84b0400..745d86b 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,4 @@ -[`ExCodable`](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 +[ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); From ca7a206ec9d1a703f52e251b645a2a8f83fd3ca6 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 11:44:57 +0800 Subject: [PATCH 15/54] Set theme jekyll-theme-cayman --- _config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/_config.yml b/_config.yml index fc24e7a..c419263 100644 --- a/_config.yml +++ b/_config.yml @@ -1 +1 @@ -theme: jekyll-theme-hacker \ No newline at end of file +theme: jekyll-theme-cayman \ No newline at end of file From 7e55f104693789bda927af8d5b678deede570669 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 11:52:56 +0800 Subject: [PATCH 16/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 745d86b..df3a869 100644 --- a/index.md +++ b/index.md @@ -28,7 +28,7 @@ GitHub 上 Star 比较多的有几种类型: - 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; - 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号。 -> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段也抛出错误,导致整个解析都出错就不好了。没错,这确实可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以我认为,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就崩给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据。 +> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种更容易出错),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以我认为,永远不要相信 API 的任何承诺,不管它返回什么,App 不要动不动就崩给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据。 抱歉废话有点多,代码上晚了: From 347ed8a84b9767cae39ebc4d5e8c9c5009e19979 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 11:53:36 +0800 Subject: [PATCH 17/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index df3a869..69518b3 100644 --- a/index.md +++ b/index.md @@ -1,6 +1,6 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 -别走!看到前面的介绍大家心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: +别走!你心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); From d3b285b54e765a20079ab87a2304e506e3a202c2 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 11:54:14 +0800 Subject: [PATCH 18/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 69518b3..895801a 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,7 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: -- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如可以与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); +- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); - 轻量,1 个文件、不到 500 行代码。 From cdbe8dd47e6b524f4cf973203bfc437fd22fa942 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 11:55:04 +0800 Subject: [PATCH 19/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 895801a..7f89fde 100644 --- a/index.md +++ b/index.md @@ -3,7 +3,7 @@ 别走!你心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; -- 支持丰富的特性,基本可以替代 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel); +- 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; - 轻量,1 个文件、不到 500 行代码。 当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 From 476435f84f2970d7ab615aaa88f967b5e39ef67c Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 18 Mar 2021 11:57:51 +0800 Subject: [PATCH 20/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 7f89fde..44d7502 100644 --- a/index.md +++ b/index.md @@ -15,7 +15,7 @@ GitHub 上 Star 比较多的有几种类型: 写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点意思。调研一番之后倾向于用 Codextended,因为抱上官方 `Codable` 的大腿。 -起初有考虑直接基于它做扩展来实现 Key-Mapping,但是后来发现受到限制较多,只能自己动手了。Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 +起初有考虑直接基于它做扩展来实现 Key-Mapping,但是受到限制较多,只能自己动手了。Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 主要特性: - 使用 `KeyPath` 映射到 Coding-Key,实现 Key-Mapping; From 22bbb748d84dbe471123ddbb2e87753d88a4df66 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Fri, 19 Mar 2021 09:17:16 +0800 Subject: [PATCH 21/54] Update index.md --- index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/index.md b/index.md index 44d7502..fdb5893 100644 --- a/index.md +++ b/index.md @@ -61,13 +61,15 @@ extension TestStruct: ExCodable { } ``` -Encode、Decode 方法使用类型推断,使代码更具可读性; +Encode、Decode; ```swift let test = TestStruct(int: 100, string: "Continue") let data = test.encoded() as Data? // Model encode to JSON + let copy1 = data?.decoded() as TestStruct? // decode JSON to Model let copy2 = TestStruct.decoded(from: data) // or Model decode from JSON + XCTAssertEqual(copy1, test) XCTAssertEqual(copy2, test) ``` From 836e7d62e7e107c10c7b0bfbae65ed50abcde39b Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Fri, 19 Mar 2021 09:20:04 +0800 Subject: [PATCH 22/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index fdb5893..b874dd9 100644 --- a/index.md +++ b/index.md @@ -61,7 +61,7 @@ extension TestStruct: ExCodable { } ``` -Encode、Decode; +Encode、Decode 也很方便; ```swift let test = TestStruct(int: 100, string: "Continue") From 1e8fa0837dcfac1548242ca7694b88588c3f356b Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Fri, 19 Mar 2021 09:54:45 +0800 Subject: [PATCH 23/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index b874dd9..f7e751a 100644 --- a/index.md +++ b/index.md @@ -1,6 +1,6 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 -别走!你心里可能想“嗷,又一个轮子”,但是这个真的有点不一样: +别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; From fee12540ae06cc8140666a5109d5f16b72fabbed Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Fri, 19 Mar 2021 18:07:55 +0800 Subject: [PATCH 24/54] Update index.md --- index.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/index.md b/index.md index f7e751a..da6194a 100644 --- a/index.md +++ b/index.md @@ -46,6 +46,8 @@ struct TestStruct: Equatable { 实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 Coding-Key 的映射,`init` 和 `encode` 方法都只要一行代码; +> 当 `encode` 方法只有这一行代码时它也是可以省略的,`ExCodable` 提供了默认实现。但是受 Swift 对初始化过程的严格限制,`init` 方法不能省略。 + ```swift extension TestStruct: ExCodable { static var keyMapping: [KeyMap] = [ @@ -55,9 +57,9 @@ extension TestStruct: ExCodable { init(from decoder: Decoder) throws { decode(with: Self.keyMapping, using: decoder) } - func encode(to encoder: Encoder) throws { - encode(with: Self.keyMapping, using: encoder) - } + // func encode(to encoder: Encoder) throws { + // encode(with: Self.keyMapping, using: encoder) + // } } ``` From 844c36a062dfbd2bb7fcda8b75eb7ccc0679e673 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Fri, 19 Mar 2021 18:08:43 +0800 Subject: [PATCH 25/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index da6194a..517a5c0 100644 --- a/index.md +++ b/index.md @@ -46,7 +46,7 @@ struct TestStruct: Equatable { 实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 Coding-Key 的映射,`init` 和 `encode` 方法都只要一行代码; -> 当 `encode` 方法只有这一行代码时它也是可以省略的,`ExCodable` 提供了默认实现。但是受 Swift 对初始化过程的严格限制,`init` 方法不能省略。 +> 当 `encode` 方法只需这一行代码时它也是可以省略的,`ExCodable` 提供了默认实现。但是受 Swift 对初始化过程的严格限制,`init` 方法不能省略。 ```swift extension TestStruct: ExCodable { From de73ab0c4b7b781d972dc38d6daeb4b4ed26bf22 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 25 Mar 2021 10:52:14 +0800 Subject: [PATCH 26/54] Update index.md --- index.md | 134 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 123 insertions(+), 11 deletions(-) diff --git a/index.md b/index.md index 517a5c0..15b79dd 100644 --- a/index.md +++ b/index.md @@ -1,23 +1,106 @@ [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: -- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多遍历,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); +- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; - 轻量,1 个文件、不到 500 行代码。 当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 -GitHub 上 Star 比较多的有几种类型: -- [SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON) 仅用于读写 JSON,至于 Model 是不管的 —— 仅适用于没有 Model 或者 Model 很简单的情况; -- Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐,[Codextended](https://github.com/JohnSundell/Codextended) 对其做了大量的简化,但还是要逐个属性 Encode/Decode —— 适用于 Model 比较简单的场景; -- [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大靠谱了,至少不够优雅。 +## Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐; -写 Objective-C 时一直享受着 [YYModel](https://github.com/ibireme/YYModel) 带来的便利,相比之下以上多少都欠了点意思。调研一番之后倾向于用 Codextended,因为抱上官方 `Codable` 的大腿。 +最基本的使用其实还是很便利的,**建议优先选用**: -起初有考虑直接基于它做扩展来实现 Key-Mapping,但是受到限制较多,只能自己动手了。Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 +```swift +struct TestAutoCodable: Codable { + private(set) var int: Int = 0 // `int` 一次 + private(set) var string: String? +} + +``` + +但是,一旦不得不手动 Encode/Decode 就麻烦了,相同字段要出现 5 次,而且还要夹杂很多其它代码: + +```swift +struct TestManualCodable: Codable { + + private(set) var int: Int = 0 // `int` 一次 + private(set) var string: String? + + enum Keys: CodingKey { + case int, i // `int` 两次 + case nested, string + } + + init(from decoder: Decoder) throws { + if let container = try? decoder.container(keyedBy: Keys.self) { + if let int = (try? container.decodeIfPresent(Int.self, forKey: Keys.int)) // `int` 三次 + ?? (try? container.decodeIfPresent(Int.self, forKey: Keys.i)) { + self.int = int // `int` 四次 + } + if let nestedContainer = try? container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested), + let string = try? nestedContainer.decodeIfPresent(String.self, forKey: Keys.string) { + self.string = string + } + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: Keys.self) + try? container.encodeIfPresent(int, forKey: Keys.int) // `int` 五次 + var nestedContainer = container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested) + try? nestedContainer.encodeIfPresent(string, forKey: Keys.string) + } + +} + +``` + +## [Codextended](https://github.com/JohnSundell/Codextended) 对 `Codable` 做了大量的简化,但还是要逐个属性 Encode/Decode: + +```swift +struct TestCodextended: Codable { + + let int: Int // `int` 一次 + let string: String? + + /* Codextended 给的例子没有这个,而是在 `init` 和 `decode` + 方法里分别写两个 `"int"`、两个 `"string"`,但那是不对的,代码 + 很多时可能会改了一个忘掉另一个 */ + enum Keys: CodingKey { + case int, i // `int` 两次 + case string + } + + init(from decoder: Decoder) throws { + int = try decoder.decodeIfPresent(Keys.int) // `int` 三次 + ?? try decoder.decodeIfPresent(Keys.i) + ?? 0 + // !!!: Codextended 目前不支持 Nested-Keys + string = try decoder.decodeIfPresent(Keys.string) + } + + func encode(to encoder: Encoder) throws { + try encoder.encode(int, for: "int") // `int` 四次 + try encoder.encode(string, for: "string") + } + +} + +``` + +## 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思: + +他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大靠谱了,至少不够优雅。 + +## ExCodable: + +调研一番之后倾向于用 Codextended,起初有考虑直接基于它做扩展来实现 Key-Mapping,但是受到限制较多,只能自己动手了。 -主要特性: +Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 + +于是便有了 `ExCodable`: - 使用 `KeyPath` 映射到 Coding-Key,实现 Key-Mapping; - 支持多个候选 Coding-Key; - 支持 Coding-Key 嵌套; @@ -30,7 +113,28 @@ GitHub 上 Star 比较多的有几种类型: > 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种更容易出错),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以我认为,永远不要相信 API 的任何承诺,不管它返回什么,App 不要动不动就崩给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据。 -抱歉废话有点多,代码上晚了: +## 上面场景,用 `ExCodable` 就简单多了: + +```swift +struct TestExCodable: ExCodable, Equatable { + + private(set) var int: Int = 0 // `int` 一次 + private(set) var string: String? + + static var keyMapping: [KeyMap] = [ + KeyMap(\.int, to: "int", "i"), // `int` 两次 + KeyMap(\.string, to: "nested.string") + ] + + init(from decoder: Decoder) throws { + decode(with: Self.keyMapping, using: decoder) + } + +} + +``` + +## `ExCodable` 用法解析: 定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; @@ -42,6 +146,7 @@ struct TestStruct: Equatable { private(set) var int: Int = 0 private(set) var string: String? } + ``` 实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 Coding-Key 的映射,`init` 和 `encode` 方法都只要一行代码; @@ -56,14 +161,19 @@ extension TestStruct: ExCodable { ] init(from decoder: Decoder) throws { decode(with: Self.keyMapping, using: decoder) + // 特殊逻辑同样可以在这里手动解决 + // 比如 `string = (int == 0) ? decoder["a"] : decoder["b"]` } // func encode(to encoder: Encoder) throws { // encode(with: Self.keyMapping, using: encoder) + // // 特殊逻辑同样可以在这里手动解决 + // // 比如 `encoder["string"] = (string == "") ? nil : string` // } } + ``` -Encode、Decode 也很方便; +序列化、反序列化也很方便; ```swift let test = TestStruct(int: 100, string: "Continue") @@ -74,11 +184,12 @@ let copy2 = TestStruct.decoded(from: data) // or Model decode from JSON XCTAssertEqual(copy1, test) XCTAssertEqual(copy2, test) + ``` 更多示例可参考 [Usage](https://github.com/iwill/ExCodable#usage) 以及单元测试代码。 -另外可以将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: +## 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: > Language: Swift > Platform: All @@ -98,6 +209,7 @@ XCTAssertEqual(copy2, test) encode(with: Self.<#keyMapping#>, using: encoder) } } + ``` ## 感谢 From 2f2c83c3732c45b1d3172b1b0ac7243840bf06f0 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 25 Mar 2021 10:53:20 +0800 Subject: [PATCH 27/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 15b79dd..d310730 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,4 @@ -[ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 +## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); From 8a2288063cf71415c42a5e2321cf67bdf0d857b4 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 25 Mar 2021 10:58:48 +0800 Subject: [PATCH 28/54] Update index.md --- index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/index.md b/index.md index d310730..a823290 100644 --- a/index.md +++ b/index.md @@ -90,9 +90,9 @@ struct TestCodextended: Codable { ``` -## 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思: +## 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等: -他们都各自构建了整套的序列化、反序列化机制,略复杂,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大靠谱了,至少不够优雅。 +他们都各自构建了整套的序列化、反序列化机制,各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思。复杂不说,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大靠谱了,至少不够优雅。 ## ExCodable: From 4c6c14fd284a3f1a4e8f34d858dce642f7a2cec9 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 25 Mar 2021 15:45:13 +0800 Subject: [PATCH 29/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index a823290..c03750b 100644 --- a/index.md +++ b/index.md @@ -92,7 +92,7 @@ struct TestCodextended: Codable { ## 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等: -他们都各自构建了整套的序列化、反序列化机制,各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思。复杂不说,甚至还有直接读写内存的(“依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”),这就不大靠谱了,至少不够优雅。 +他们都各自构建了整套的序列化、反序列化机制,各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思。与 `Codable` 不兼容、代码很复杂不说,甚至还有直接读写内存的 —— “依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”,这就不大靠谱了,至少不够优雅。 ## ExCodable: From 2e1e461353d0eeddcb552fce538155b00e4f30a6 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 22 Apr 2021 10:39:31 +0800 Subject: [PATCH 30/54] Update index.md --- index.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/index.md b/index.md index c03750b..0c21d4d 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,11 @@ ## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: -- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利,比如与 [Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接,参考 [Response Decodable Handler](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler); +- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接; +> [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) +> [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) +> [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) +> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; - 轻量,1 个文件、不到 500 行代码。 @@ -215,3 +219,5 @@ XCTAssertEqual(copy2, test) ## 感谢 在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 + +`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈 - i+ExCodable@iwill.im。 From 83591fe4223b508c9b019bc4c7232e1a8a8b4272 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 22 Apr 2021 10:40:53 +0800 Subject: [PATCH 31/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 0c21d4d..4ab0a72 100644 --- a/index.md +++ b/index.md @@ -5,7 +5,7 @@ > [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) > [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) > [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) -> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) +> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; - 轻量,1 个文件、不到 500 行代码。 From b911331d02e57c89f9835ee0c265ccc6562f5d5f Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 22 Apr 2021 10:41:39 +0800 Subject: [PATCH 32/54] Update index.md --- index.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/index.md b/index.md index 4ab0a72..848a887 100644 --- a/index.md +++ b/index.md @@ -2,10 +2,14 @@ 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接; -> [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) -> [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) -> [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) -> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) +> [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) +> +> [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) +> +> [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) +> +> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) +> - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; - 轻量,1 个文件、不到 500 行代码。 From ab0079029a2e7963336e94efdc7043b89f5df794 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 22 Apr 2021 10:58:09 +0800 Subject: [PATCH 33/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index 848a887..4511220 100644 --- a/index.md +++ b/index.md @@ -224,4 +224,4 @@ XCTAssertEqual(copy2, test) 在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 -`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈 - i+ExCodable@iwill.im。 +`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈 - [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 From dce956b0c12d184d91e34720e002e4fc96e844f0 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 22 Apr 2021 11:56:20 +0800 Subject: [PATCH 34/54] Update index.md --- index.md | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/index.md b/index.md index 4511220..88c4c99 100644 --- a/index.md +++ b/index.md @@ -1,7 +1,7 @@ ## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: -- `ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 无缝对接; +- `ExCodable` 是在 Swift `Codable` 基础上的扩展,**可以享受到诸多便利**,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 等无缝对接 —— `ExCodable` 没有做任何额外处理,躺赚那种; > [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) > > [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) @@ -12,7 +12,7 @@ > - 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; - 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; -- 轻量,1 个文件、不到 500 行代码。 +- 轻量,1 个文件、500 行代码。 当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 @@ -28,7 +28,7 @@ struct TestAutoCodable: Codable { ``` -但是,一旦不得不手动 Encode/Decode 就麻烦了,相同字段要出现 5 次,而且还要夹杂很多其它代码: +但是,一旦不得不手动 Encode/Decode 就完蛋了,相同字段要出现 5 次,而且还要夹杂很多其它代码: ```swift struct TestManualCodable: Codable { @@ -37,7 +37,7 @@ struct TestManualCodable: Codable { private(set) var string: String? enum Keys: CodingKey { - case int, i // `int` 两次 + case int, i // `int` 两次,这里省掉,在第三次、第五次的那里直接写字符串?不要! case nested, string } @@ -60,7 +60,6 @@ struct TestManualCodable: Codable { var nestedContainer = container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested) try? nestedContainer.encodeIfPresent(string, forKey: Keys.string) } - } ``` @@ -93,7 +92,6 @@ struct TestCodextended: Codable { try encoder.encode(int, for: "int") // `int` 四次 try encoder.encode(string, for: "string") } - } ``` @@ -117,9 +115,11 @@ Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 ` - 支持 `struct`、`class`、subclass; - 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; - 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; -- 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号。 +- 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号 —— 现在也支持抛出异常了 [可选]。 -> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种更容易出错),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以我认为,永远不要相信 API 的任何承诺,不管它返回什么,App 不要动不动就崩给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据。 +> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种更容易出错),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就死给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据,而不是依赖异常。 + +> 为了满足不同的编程习惯,`ExCodable` - 0.5.0 版本开始支持了个别/全部字段是否非空 - `nonnull`(Encode/Decode 时是否使用带有 `IfPresent` 的方法)、以及遇到异常时是否抛出 - `throws`。这两个参数都是 `Bool` 类型,组合使用可以产生不同的效果。比如某内嵌的对象指定某字段 `nonnull = true`、`throws = false`,遇到非空字段无法解析会导致该字段所属对象为 `nil`,但如果它外层对象没有指定该对象 `nonnull = true`,则会继续解析其它字段,而不是完全终止解析。 ## 上面场景,用 `ExCodable` 就简单多了: @@ -137,7 +137,6 @@ struct TestExCodable: ExCodable, Equatable { init(from decoder: Decoder) throws { decode(with: Self.keyMapping, using: decoder) } - } ``` From 37a9fa1d2b439c5cffc164a580001ac965f82f1e Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Thu, 22 Apr 2021 11:58:47 +0800 Subject: [PATCH 35/54] Update index.md --- index.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/index.md b/index.md index 88c4c99..ec3ee17 100644 --- a/index.md +++ b/index.md @@ -135,7 +135,7 @@ struct TestExCodable: ExCodable, Equatable { ] init(from decoder: Decoder) throws { - decode(with: Self.keyMapping, using: decoder) + decode(from: decoder, with: Self.keyMapping) } } @@ -167,12 +167,12 @@ extension TestStruct: ExCodable { KeyMap(\.string, to: "nested.string") ] init(from decoder: Decoder) throws { - decode(with: Self.keyMapping, using: decoder) + decode(from: decoder, with: Self.keyMapping) // 特殊逻辑同样可以在这里手动解决 // 比如 `string = (int == 0) ? decoder["a"] : decoder["b"]` } // func encode(to encoder: Encoder) throws { - // encode(with: Self.keyMapping, using: encoder) + // encode(to: encoder, with: Self.keyMapping) // // 特殊逻辑同样可以在这里手动解决 // // 比如 `encoder["string"] = (string == "") ? nil : string` // } @@ -210,10 +210,10 @@ XCTAssertEqual(copy2, test) <#...#> ] init(from decoder: Decoder) throws { - decode<#Reference#>(with: Self.<#keyMapping#>, using: decoder) + decode<#Reference#>(from: decoder, with: Self.<#keyMapping#>) } func encode(to encoder: Encoder) throws { - encode(with: Self.<#keyMapping#>, using: encoder) + encode(to: encoder, with: Self.<#keyMapping#>) } } From 23209370f643025da67edc765d7e8b2fc9a82ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=2E=20M=C3=ADng?= Date: Fri, 4 Feb 2022 14:19:30 +0800 Subject: [PATCH 36/54] Update index.md --- index.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/index.md b/index.md index ec3ee17..f3680f5 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,6 @@ +ExCodable +======== + ## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: From 175ccc2ee9d2c19f9c5daed0ffca5830ab613124 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=2E=20M=C3=ADng?= Date: Fri, 4 Feb 2022 14:21:11 +0800 Subject: [PATCH 37/54] Update index.md --- index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.md b/index.md index f3680f5..0d7d0a6 100644 --- a/index.md +++ b/index.md @@ -226,4 +226,4 @@ XCTAssertEqual(copy2, test) 在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 -`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈 - [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 +`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈:[i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 From c32de120fe353e9e234a41e9e01869711b3dd5dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=2E=20M=C3=ADng?= Date: Fri, 4 Feb 2022 15:30:37 +0800 Subject: [PATCH 38/54] Update index.md --- index.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/index.md b/index.md index 0d7d0a6..d553bd9 100644 --- a/index.md +++ b/index.md @@ -1,6 +1,26 @@ ExCodable ======== +[![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/iwill/ExCodable#readme) + +[![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org/) +[![Swift Package Manager](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/) +[![Platforms](https://img.shields.io/cocoapods/p/ExCodable.svg)](#) +
+[![Build and Test](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml) +[![GitHub Releases (latest SemVer)](https://img.shields.io/github/v/release/iwill/ExCodable.svg?sort=semver)](https://github.com/iwill/ExCodable/releases) +[![Deploy to CocoaPods](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml) +[![Cocoapods](https://img.shields.io/cocoapods/v/ExCodable.svg)](https://cocoapods.org/pods/ExCodable) +
+[![LICENSE](https://img.shields.io/github/license/iwill/ExCodable.svg)](https://github.com/iwill/ExCodable/blob/master/LICENSE) +[![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) + +[En](https://github.com/iwill/ExCodable) | 中文 + +-------- + +> 0.x + ## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: From ddec60046acc246e842ce05e775888047a70971a Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Mon, 16 Aug 2021 17:06:39 +0800 Subject: [PATCH 39/54] 0.x.md backup --- index.md => 0.x.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename index.md => 0.x.md (100%) diff --git a/index.md b/0.x.md similarity index 100% rename from index.md rename to 0.x.md From 52198cb4efd724539bcf25fcc47fa6eada5dd6b0 Mon Sep 17 00:00:00 2001 From: "Mr. Ming" Date: Mon, 16 Aug 2021 17:07:39 +0800 Subject: [PATCH 40/54] 1.x.md init --- index.md | 226 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 index.md diff --git a/index.md b/index.md new file mode 100644 index 0000000..ec3ee17 --- /dev/null +++ b/index.md @@ -0,0 +1,226 @@ +## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 + +别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: +- `ExCodable` 是在 Swift `Codable` 基础上的扩展,**可以享受到诸多便利**,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 等无缝对接 —— `ExCodable` 没有做任何额外处理,躺赚那种; +> [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) +> +> [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) +> +> [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) +> +> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) +> +- 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; +- 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; +- 轻量,1 个文件、500 行代码。 + +当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 + +## Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐; + +最基本的使用其实还是很便利的,**建议优先选用**: + +```swift +struct TestAutoCodable: Codable { + private(set) var int: Int = 0 // `int` 一次 + private(set) var string: String? +} + +``` + +但是,一旦不得不手动 Encode/Decode 就完蛋了,相同字段要出现 5 次,而且还要夹杂很多其它代码: + +```swift +struct TestManualCodable: Codable { + + private(set) var int: Int = 0 // `int` 一次 + private(set) var string: String? + + enum Keys: CodingKey { + case int, i // `int` 两次,这里省掉,在第三次、第五次的那里直接写字符串?不要! + case nested, string + } + + init(from decoder: Decoder) throws { + if let container = try? decoder.container(keyedBy: Keys.self) { + if let int = (try? container.decodeIfPresent(Int.self, forKey: Keys.int)) // `int` 三次 + ?? (try? container.decodeIfPresent(Int.self, forKey: Keys.i)) { + self.int = int // `int` 四次 + } + if let nestedContainer = try? container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested), + let string = try? nestedContainer.decodeIfPresent(String.self, forKey: Keys.string) { + self.string = string + } + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: Keys.self) + try? container.encodeIfPresent(int, forKey: Keys.int) // `int` 五次 + var nestedContainer = container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested) + try? nestedContainer.encodeIfPresent(string, forKey: Keys.string) + } +} + +``` + +## [Codextended](https://github.com/JohnSundell/Codextended) 对 `Codable` 做了大量的简化,但还是要逐个属性 Encode/Decode: + +```swift +struct TestCodextended: Codable { + + let int: Int // `int` 一次 + let string: String? + + /* Codextended 给的例子没有这个,而是在 `init` 和 `decode` + 方法里分别写两个 `"int"`、两个 `"string"`,但那是不对的,代码 + 很多时可能会改了一个忘掉另一个 */ + enum Keys: CodingKey { + case int, i // `int` 两次 + case string + } + + init(from decoder: Decoder) throws { + int = try decoder.decodeIfPresent(Keys.int) // `int` 三次 + ?? try decoder.decodeIfPresent(Keys.i) + ?? 0 + // !!!: Codextended 目前不支持 Nested-Keys + string = try decoder.decodeIfPresent(Keys.string) + } + + func encode(to encoder: Encoder) throws { + try encoder.encode(int, for: "int") // `int` 四次 + try encoder.encode(string, for: "string") + } +} + +``` + +## 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等: + +他们都各自构建了整套的序列化、反序列化机制,各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思。与 `Codable` 不兼容、代码很复杂不说,甚至还有直接读写内存的 —— “依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”,这就不大靠谱了,至少不够优雅。 + +## ExCodable: + +调研一番之后倾向于用 Codextended,起初有考虑直接基于它做扩展来实现 Key-Mapping,但是受到限制较多,只能自己动手了。 + +Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 + +于是便有了 `ExCodable`: +- 使用 `KeyPath` 映射到 Coding-Key,实现 Key-Mapping; +- 支持多个候选 Coding-Key; +- 支持 Coding-Key 嵌套; +- 支持使用 Subscript 进行自定义 Encode/Decode; +- 支持数据类型自动转换以及自定义转换; +- 支持 `struct`、`class`、subclass; +- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; +- 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; +- 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号 —— 现在也支持抛出异常了 [可选]。 + +> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种更容易出错),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就死给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据,而不是依赖异常。 + +> 为了满足不同的编程习惯,`ExCodable` - 0.5.0 版本开始支持了个别/全部字段是否非空 - `nonnull`(Encode/Decode 时是否使用带有 `IfPresent` 的方法)、以及遇到异常时是否抛出 - `throws`。这两个参数都是 `Bool` 类型,组合使用可以产生不同的效果。比如某内嵌的对象指定某字段 `nonnull = true`、`throws = false`,遇到非空字段无法解析会导致该字段所属对象为 `nil`,但如果它外层对象没有指定该对象 `nonnull = true`,则会继续解析其它字段,而不是完全终止解析。 + +## 上面场景,用 `ExCodable` 就简单多了: + +```swift +struct TestExCodable: ExCodable, Equatable { + + private(set) var int: Int = 0 // `int` 一次 + private(set) var string: String? + + static var keyMapping: [KeyMap] = [ + KeyMap(\.int, to: "int", "i"), // `int` 两次 + KeyMap(\.string, to: "nested.string") + ] + + init(from decoder: Decoder) throws { + decode(from: decoder, with: Self.keyMapping) + } +} + +``` + +## `ExCodable` 用法解析: + +定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; + +> `Optional` 类型不需要默认值; +> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/iwill/ExCodable#usage); + +```swift +struct TestStruct: Equatable { + private(set) var int: Int = 0 + private(set) var string: String? +} + +``` + +实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 Coding-Key 的映射,`init` 和 `encode` 方法都只要一行代码; + +> 当 `encode` 方法只需这一行代码时它也是可以省略的,`ExCodable` 提供了默认实现。但是受 Swift 对初始化过程的严格限制,`init` 方法不能省略。 + +```swift +extension TestStruct: ExCodable { + static var keyMapping: [KeyMap] = [ + KeyMap(\.int, to: "int", "i"), + KeyMap(\.string, to: "nested.string") + ] + init(from decoder: Decoder) throws { + decode(from: decoder, with: Self.keyMapping) + // 特殊逻辑同样可以在这里手动解决 + // 比如 `string = (int == 0) ? decoder["a"] : decoder["b"]` + } + // func encode(to encoder: Encoder) throws { + // encode(to: encoder, with: Self.keyMapping) + // // 特殊逻辑同样可以在这里手动解决 + // // 比如 `encoder["string"] = (string == "") ? nil : string` + // } +} + +``` + +序列化、反序列化也很方便; + +```swift +let test = TestStruct(int: 100, string: "Continue") +let data = test.encoded() as Data? // Model encode to JSON + +let copy1 = data?.decoded() as TestStruct? // decode JSON to Model +let copy2 = TestStruct.decoded(from: data) // or Model decode from JSON + +XCTAssertEqual(copy1, test) +XCTAssertEqual(copy2, test) + +``` + +更多示例可参考 [Usage](https://github.com/iwill/ExCodable#usage) 以及单元测试代码。 + +## 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: + +> Language: Swift +> Platform: All +> Completion: ExCodable +> Availability: Top Level + +```swift +<#extension/struct/class#> <#Type#>: ExCodable { + static var <#keyMapping#>: [KeyMap<<#SelfType#>>] = [ + KeyMap(\.<#property#>, to: <#"key"#>), + <#...#> + ] + init(from decoder: Decoder) throws { + decode<#Reference#>(from: decoder, with: Self.<#keyMapping#>) + } + func encode(to encoder: Encoder) throws { + encode(to: encoder, with: Self.<#keyMapping#>) + } +} + +``` + +## 感谢 + +在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 + +`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈 - [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 From 6c272f43a3aea8d0726654e69351107af1605b74 Mon Sep 17 00:00:00 2001 From: iwill Date: Thu, 9 Dec 2021 15:18:24 +0800 Subject: [PATCH 41/54] temp --- index.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/index.md b/index.md index ec3ee17..56db7e7 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,21 @@ +[![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/iwill/ExCodable#readme) + +[![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org/) +[![Swift Package Manager](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/) +[![Platforms](https://img.shields.io/cocoapods/p/ExCodable.svg)](#) +
+[![Build and Test](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml) +[![GitHub Releases (latest SemVer)](https://img.shields.io/github/v/release/iwill/ExCodable.svg?sort=semver)](https://github.com/iwill/ExCodable/releases) +[![Deploy to CocoaPods](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml) +[![Cocoapods](https://img.shields.io/cocoapods/v/ExCodable.svg)](https://cocoapods.org/pods/ExCodable) +
+[![LICENSE](https://img.shields.io/github/license/iwill/ExCodable.svg)](https://github.com/iwill/ExCodable/blob/master/LICENSE) +[![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) + +[En](https://github.com/iwill/ExCodable) | 中文 + + + ## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: From b3877ce7b52fdc98b78a48ea8b6bd6d2e35adde2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Sun, 10 Dec 2023 13:58:02 +0800 Subject: [PATCH 42/54] temp --- index.md | 75 ++++++++++++++++++++++++++++---------------------------- 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/index.md b/index.md index 56db7e7..c8436a5 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,6 @@ +ExCodable +======== + [![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/iwill/ExCodable#readme) [![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org/) @@ -14,29 +17,38 @@ [En](https://github.com/iwill/ExCodable) | 中文 +## [ExCodable](https://github.com/iwill/ExCodable) 是一个 Swift 版 JSON-Model 转换工具 +> 去年春节完成了 0.x 版本,后来有一次巨大的升级,今年春节终于发布了 1.0 版本。这该死的拖延症 ... -## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 +若非必要,勿造轮子。但显然,我又造了一个,所以它一定是必要的: -别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: -- `ExCodable` 是在 Swift `Codable` 基础上的扩展,**可以享受到诸多便利**,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 等无缝对接 —— `ExCodable` 没有做任何额外处理,躺赚那种; -> [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) -> -> [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) -> -> [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) -> -> [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) -> -- 基于 `KeyPath` 实现 Key-Mapping,无需逐个属性 Encode/Decode; -- 支持丰富的特性,差不多实现了 Objective-C 版的 [YYModel](https://github.com/ibireme/YYModel) 的所有特性; +- **`ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利!** 比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 等无缝对接 —— `ExCodable` 没有做任何额外处理,躺赚那种: + - [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) + - [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) + - [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) + - [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) +- 基于 `@propertyWrapper` 实现 JSON-Key 到 Property 的映射,无需逐个属性 Encode/Decode; +- 支持多个候选 JSON-Key,依次解析; +- 支持使用包含 `.` 的 JSON-Key 访问嵌套多层的 JSON 数据结构; +- 支持使用 Subscript 进行手动的 Encode/Decode; +- 支持数据类型自动转换以及自定义转换; +- 支持更灵活的异常处理。 // 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号 —— 现在也支持抛出异常了 [可选]。 + - 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种错误更常见),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道是不是有个新“发现”是什么体验,老司机可以回忆一下 YYModel 出现之前的岁月。所以,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就死给人看,这严重影响到开发者的声誉!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据,而不是依赖异常。 + - 为了满足不同的编程习惯,`ExCodable` - 0.5.0 版本开始支持个别/全部字段是否非空 - `nonnull`(Encode/Decode 时是否使用带有 `IfPresent` 的方法)、以及遇到异常时是否抛出 - `throws`。这两个参数都是 `Bool` 类型,组合使用可以产生不同的效果。比如某内嵌的对象指定某字段 `nonnull = true`、`throws = false`,遇到非空字段无法解析会导致该字段所属对象为 `nil`,但如果它外层对象没有指定该对象 `nonnull = true`,则会继续解析其它字段,而不是完全终止解析。 +- 支持 `CodingKey`; +- 支持 `struct`、`class` 以及继承; +- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; +- 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; - 轻量,1 个文件、500 行代码。 -当然我不是一开始就决定要造轮子的。近期我们团队准备开始使用 Swift,节前开始寻找一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念,但是 JSON-Model 转换并没有找到合适的。 +## 为什么造轮子 + +当然,我不是一开始就要造轮子。我们团队准备开始使用 Swift,我找了一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念。但是 JSON-Model 转换并没有找到合适的,没有一个能完全取代 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel)。 -## Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐; +### Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐; -最基本的使用其实还是很便利的,**建议优先选用**: +最基本的使用其实还是很便利的: ```swift struct TestAutoCodable: Codable { @@ -82,7 +94,7 @@ struct TestManualCodable: Codable { ``` -## [Codextended](https://github.com/JohnSundell/Codextended) 对 `Codable` 做了大量的简化,但还是要逐个属性 Encode/Decode: +### [Codextended](https://github.com/JohnSundell/Codextended) 对 `Codable` 做了大量的简化,但还是要逐个属性 Encode/Decode: ```swift struct TestCodextended: Codable { @@ -114,32 +126,19 @@ struct TestCodextended: Codable { ``` -## 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等: +### 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等: 他们都各自构建了整套的序列化、反序列化机制,各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思。与 `Codable` 不兼容、代码很复杂不说,甚至还有直接读写内存的 —— “依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”,这就不大靠谱了,至少不够优雅。 -## ExCodable: +## ExCodable 调研一番之后倾向于用 Codextended,起初有考虑直接基于它做扩展来实现 Key-Mapping,但是受到限制较多,只能自己动手了。 Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 -于是便有了 `ExCodable`: -- 使用 `KeyPath` 映射到 Coding-Key,实现 Key-Mapping; -- 支持多个候选 Coding-Key; -- 支持 Coding-Key 嵌套; -- 支持使用 Subscript 进行自定义 Encode/Decode; -- 支持数据类型自动转换以及自定义转换; -- 支持 `struct`、`class`、subclass; -- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; -- 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; -- 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号 —— 现在也支持抛出异常了 [可选]。 - -> 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种更容易出错),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道经常有新“发现”是什么样的体验。老司机可以回忆一下 YYModel 出现之前的岁月。所以,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就死给人看,这会严重影响一个开发者的名声!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据,而不是依赖异常。 - -> 为了满足不同的编程习惯,`ExCodable` - 0.5.0 版本开始支持了个别/全部字段是否非空 - `nonnull`(Encode/Decode 时是否使用带有 `IfPresent` 的方法)、以及遇到异常时是否抛出 - `throws`。这两个参数都是 `Bool` 类型,组合使用可以产生不同的效果。比如某内嵌的对象指定某字段 `nonnull = true`、`throws = false`,遇到非空字段无法解析会导致该字段所属对象为 `nil`,但如果它外层对象没有指定该对象 `nonnull = true`,则会继续解析其它字段,而不是完全终止解析。 +于是便有了 `ExCodable`! -## 上面场景,用 `ExCodable` 就简单多了: +### 上面场景,用 `ExCodable` 就简单多了: ```swift struct TestExCodable: ExCodable, Equatable { @@ -159,7 +158,7 @@ struct TestExCodable: ExCodable, Equatable { ``` -## `ExCodable` 用法解析: +### `ExCodable` 用法解析: 定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; @@ -174,7 +173,7 @@ struct TestStruct: Equatable { ``` -实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 Coding-Key 的映射,`init` 和 `encode` 方法都只要一行代码; +实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 JSON-Key 的映射,`init` 和 `encode` 方法都只要一行代码; > 当 `encode` 方法只需这一行代码时它也是可以省略的,`ExCodable` 提供了默认实现。但是受 Swift 对初始化过程的严格限制,`init` 方法不能省略。 @@ -214,7 +213,7 @@ XCTAssertEqual(copy2, test) 更多示例可参考 [Usage](https://github.com/iwill/ExCodable#usage) 以及单元测试代码。 -## 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: +### 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: > Language: Swift > Platform: All @@ -241,4 +240,4 @@ XCTAssertEqual(copy2, test) 在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 -`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈 - [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 +`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈:[i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 From 00ac01c8c19b701e4b23e4e86bd64e520f2d6544 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Fri, 2 Aug 2024 01:30:05 +0800 Subject: [PATCH 43/54] meta: 0.x --- 0.x.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/0.x.md b/0.x.md index d553bd9..192eaf0 100644 --- a/0.x.md +++ b/0.x.md @@ -1,27 +1,28 @@ ExCodable ======== -[![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/iwill/ExCodable#readme) +[![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/ExCodable/ExCodable#readme) [![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org/) [![Swift Package Manager](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/) [![Platforms](https://img.shields.io/cocoapods/p/ExCodable.svg)](#)
-[![Build and Test](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml) -[![GitHub Releases (latest SemVer)](https://img.shields.io/github/v/release/iwill/ExCodable.svg?sort=semver)](https://github.com/iwill/ExCodable/releases) -[![Deploy to CocoaPods](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml) +[![Build and Test](https://github.com/ExCodable/ExCodable/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/ExCodable/ExCodable/actions/workflows/build-and-test.yml) +[![GitHub Releases (latest SemVer)](https://img.shields.io/github/v/release/ExCodable/ExCodable.svg?sort=semver)](https://github.com/ExCodable/ExCodable/releases) +[![Deploy to CocoaPods](https://github.com/ExCodable/ExCodable/actions/workflows/deploy_to_cocoapods.yml/badge.svg)](https://github.com/ExCodable/ExCodable/actions/workflows/deploy_to_cocoapods.yml) [![Cocoapods](https://img.shields.io/cocoapods/v/ExCodable.svg)](https://cocoapods.org/pods/ExCodable)
-[![LICENSE](https://img.shields.io/github/license/iwill/ExCodable.svg)](https://github.com/iwill/ExCodable/blob/master/LICENSE) +[![LICENSE](https://img.shields.io/github/license/ExCodable/ExCodable.svg)](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) [![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) -[En](https://github.com/iwill/ExCodable) | 中文 +[En](https://github.com/ExCodable/ExCodable) | 中文 -------- -> 0.x +> - **1.x**: New Released +> - 0.x: DEPRECATED -## [ExCodable](https://github.com/iwill/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 +## [ExCodable](https://github.com/ExCodable/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 别走!你可能会想“嗷,又一个轮子”,但是这个真的有点不一样: - `ExCodable` 是在 Swift `Codable` 基础上的扩展,**可以享受到诸多便利**,比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 等无缝对接 —— `ExCodable` 没有做任何额外处理,躺赚那种; @@ -169,7 +170,7 @@ struct TestExCodable: ExCodable, Equatable { 定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; > `Optional` 类型不需要默认值; -> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/iwill/ExCodable#usage); +> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/ExCodable/ExCodable#usage); ```swift struct TestStruct: Equatable { @@ -217,7 +218,7 @@ XCTAssertEqual(copy2, test) ``` -更多示例可参考 [Usage](https://github.com/iwill/ExCodable#usage) 以及单元测试代码。 +更多示例可参考 [Usage](https://github.com/ExCodable/ExCodable#usage) 以及单元测试代码。 ## 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: From 255d4698b21c9d85efbd000bd6ebba0931e7c12c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Fri, 2 Aug 2024 00:55:08 +0800 Subject: [PATCH 44/54] meta: gitignore --- .gitignore | 134 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 134 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..85fc6ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,134 @@ + +# Created by https://www.toptal.com/developers/gitignore/api/osx,xcode,swift +# Edit at https://www.toptal.com/developers/gitignore?templates=osx,xcode,swift + +### OSX ### +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Swift ### +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Obj-C/Swift specific +*.hmap + +## App packaging +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +# *.xcodeproj +# Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata +# hence it is not needed unless you have added a package configuration file to your project +.swiftpm + +.build/ + +# CocoaPods +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +Pods/ +# Add this line if you want to avoid checking in source code from the Xcode workspace +# *.xcworkspace + +# Carthage +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build/ + +# Accio dependency management +Dependencies/ +.accio/ + +# fastlane +# It is recommended to not store the screenshots in the git repo. +# Instead, use fastlane to re-generate the screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output + +# Code Injection +# After new code Injection tools there's a generated folder /iOSInjectionProject +# https://github.com/johnno1962/injectionforxcode + +iOSInjectionProject/ + +### Xcode ### +# Xcode +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + + + + +## Gcc Patch +/*.gcno + +### Xcode Patch ### +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcworkspace/contents.xcworkspacedata +**/xcshareddata/WorkspaceSettings.xcsettings + +# End of https://www.toptal.com/developers/gitignore/api/osx,xcode,swift From 7467f1210f1c93e9e43cde38e6757d58c09baaee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Fri, 2 Aug 2024 01:18:43 +0800 Subject: [PATCH 45/54] meta: 0.x -> 1.x --- index.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/index.md b/index.md index c8436a5..ee0c70a 100644 --- a/index.md +++ b/index.md @@ -1,23 +1,23 @@ ExCodable ======== -[![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/iwill/ExCodable#readme) +[![ExCodable](https://iwill.im/images/ExCodable-v1-1920x500.png)](https://github.com/ExCodable/ExCodable#readme) [![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org/) [![Swift Package Manager](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/) [![Platforms](https://img.shields.io/cocoapods/p/ExCodable.svg)](#)
-[![Build and Test](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/build-and-test.yml) -[![GitHub Releases (latest SemVer)](https://img.shields.io/github/v/release/iwill/ExCodable.svg?sort=semver)](https://github.com/iwill/ExCodable/releases) -[![Deploy to CocoaPods](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml/badge.svg)](https://github.com/iwill/ExCodable/actions/workflows/deploy_to_cocoapods.yml) +[![Build and Test](https://github.com/ExCodable/ExCodable/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/ExCodable/ExCodable/actions/workflows/build-and-test.yml) +[![GitHub Releases (latest SemVer)](https://img.shields.io/github/v/release/ExCodable/ExCodable.svg?sort=semver)](https://github.com/ExCodable/ExCodable/releases) +[![Deploy to CocoaPods](https://github.com/ExCodable/ExCodable/actions/workflows/deploy_to_cocoapods.yml/badge.svg)](https://github.com/ExCodable/ExCodable/actions/workflows/deploy_to_cocoapods.yml) [![Cocoapods](https://img.shields.io/cocoapods/v/ExCodable.svg)](https://cocoapods.org/pods/ExCodable)
-[![LICENSE](https://img.shields.io/github/license/iwill/ExCodable.svg)](https://github.com/iwill/ExCodable/blob/master/LICENSE) +[![LICENSE](https://img.shields.io/github/license/ExCodable/ExCodable.svg)](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) [![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) -[En](https://github.com/iwill/ExCodable) | 中文 +[En](https://github.com/ExCodable/ExCodable) | 中文 -## [ExCodable](https://github.com/iwill/ExCodable) 是一个 Swift 版 JSON-Model 转换工具 +## [ExCodable](https://github.com/ExCodable/ExCodable) 是一个 Swift 版 JSON-Model 转换工具 > 去年春节完成了 0.x 版本,后来有一次巨大的升级,今年春节终于发布了 1.0 版本。这该死的拖延症 ... @@ -163,7 +163,7 @@ struct TestExCodable: ExCodable, Equatable { 定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; > `Optional` 类型不需要默认值; -> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/iwill/ExCodable#usage); +> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/ExCodable/ExCodable#usage); ```swift struct TestStruct: Equatable { @@ -211,7 +211,7 @@ XCTAssertEqual(copy2, test) ``` -更多示例可参考 [Usage](https://github.com/iwill/ExCodable#usage) 以及单元测试代码。 +更多示例可参考 [Usage](https://github.com/ExCodable/ExCodable#usage) 以及单元测试代码。 ### 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: From a3db0c71f7461d3dcde54206ba9dcd1a44d24275 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Mon, 5 Aug 2024 03:00:48 +0800 Subject: [PATCH 46/54] meta: 1.x --- index.md | 439 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 298 insertions(+), 141 deletions(-) diff --git a/index.md b/index.md index ee0c70a..76dd9c7 100644 --- a/index.md +++ b/index.md @@ -1,9 +1,9 @@ ExCodable ======== -[![ExCodable](https://iwill.im/images/ExCodable-v1-1920x500.png)](https://github.com/ExCodable/ExCodable#readme) +[![ExCodable](https://iwill.im/images/ExCodable-1.x-1920x500.png)](https://github.com/ExCodable/ExCodable#readme) -[![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org/) +[![Swift 5.10](https://img.shields.io/badge/Swift-5.10-orange.svg)](https://swift.org/) [![Swift Package Manager](https://img.shields.io/badge/spm-compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/) [![Platforms](https://img.shields.io/cocoapods/p/ExCodable.svg)](#)
@@ -17,227 +17,384 @@ ExCodable [En](https://github.com/ExCodable/ExCodable) | 中文 -## [ExCodable](https://github.com/ExCodable/ExCodable) 是一个 Swift 版 JSON-Model 转换工具 +ExCodable 是一个 Swift 版 JSON-Model 转换工具,现在迎来重要升级,发布 1.0 版本。 -> 去年春节完成了 0.x 版本,后来有一次巨大的升级,今年春节终于发布了 1.0 版本。这该死的拖延症 ... +「若非必要,勿造轮子」。但显然,我又造了一个,所以它一定是必要的: -若非必要,勿造轮子。但显然,我又造了一个,所以它一定是必要的: +- Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐 +- [Codextended](https://github.com/JohnSundell/Codextended) 想法特别好,对 `Codable` 做了大量的简化,但还是要逐个属性 encode/decode +- 其它 star 较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等(不知道最近两年有没有新的),不兼容 `Codable`,语法奇怪、繁琐,甚至还有读写内存的,说实话 —— 丑得要死! -- **`ExCodable` 是在 Swift `Codable` 基础上的扩展,可以享受到诸多便利!** 比如与 `NSCoding`、[Alamofire](https://github.com/Alamofire/Alamofire) 等无缝对接 —— `ExCodable` 没有做任何额外处理,躺赚那种: - - [An Answer from Stack Overflow](https://stackoverflow.com/a/49952202/456536) - - [NSKeyedArchiver.encodeEncodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedarchiver/2924373-encodeencodable) - - [NSKeyedUnarchiver.decodeTopLevelDecodable(_:forKey:)](https://developer.apple.com/documentation/foundation/nskeyedunarchiver/2924375-decodetopleveldecodable) - - [Response Decodable Handler from Alamofire](https://github.com/Alamofire/Alamofire/blob/master/Documentation/Usage.md#response-decodable-handler) -- 基于 `@propertyWrapper` 实现 JSON-Key 到 Property 的映射,无需逐个属性 Encode/Decode; -- 支持多个候选 JSON-Key,依次解析; -- 支持使用包含 `.` 的 JSON-Key 访问嵌套多层的 JSON 数据结构; -- 支持使用 Subscript 进行手动的 Encode/Decode; -- 支持数据类型自动转换以及自定义转换; -- 支持更灵活的异常处理。 // 使用 `Optional` 类型取代抛出没什么用的 `error`,避免到处写 `try?`,有时还要套上括号 —— 现在也支持抛出异常了 [可选]。 - - 这里要多说两句:一般情况下抛出错误是有用的,但是在 JSON-Model 转换的场景略有不同。经常遇到的错误无非就是字段少了、类型错了。如果是关键数据有问题抛出错误也还好,但是有时不痛不痒的字段出错(这种错误更常见),导致整个解析都失败就不好了。确实这样可以及时发现返回结果中的问题,但是大家可能也知道是不是有个新“发现”是什么体验,老司机可以回忆一下 YYModel 出现之前的岁月。所以,永远不要相信关于 API 的任何承诺,不管它返回什么,App 不要动不动就死给人看,这严重影响到开发者的声誉!可能有人会问,它真的给你返回一坨🍦怎么办?可以加个关键数据校验环节,只校验关键数据,而不是依赖异常。 - - 为了满足不同的编程习惯,`ExCodable` - 0.5.0 版本开始支持个别/全部字段是否非空 - `nonnull`(Encode/Decode 时是否使用带有 `IfPresent` 的方法)、以及遇到异常时是否抛出 - `throws`。这两个参数都是 `Bool` 类型,组合使用可以产生不同的效果。比如某内嵌的对象指定某字段 `nonnull = true`、`throws = false`,遇到非空字段无法解析会导致该字段所属对象为 `nil`,但如果它外层对象没有指定该对象 `nonnull = true`,则会继续解析其它字段,而不是完全终止解析。 -- 支持 `CodingKey`; -- 支持 `struct`、`class` 以及继承; -- 支持 JSON、PList 以及自定义 Encoder/Decoder,默认使用 JSON; -- 使用类型推断,支持 `Data`、`String`、`Array`、`Object` 类型 JSON 数据; -- 轻量,1 个文件、500 行代码。 +ObjC 时代,最好的 JSON-Model 转换非 [YYModel](https://github.com/ibireme/YYModel) 莫属,可惜没有 Swift 版,所以 [自己写一个吧](./0.x.md/#excodable-是我在春节期间带娃之余用了几个晚上完成的一个-swift-版的-json-model-转换工具)。 -## 为什么造轮子 +## 主要特性 -当然,我不是一开始就要造轮子。我们团队准备开始使用 Swift,我找了一些开源框架。网络请求用 [Alamofire](https://github.com/Alamofire/Alamofire)、自动布局用 [SnapKit](https://github.com/SnapKit/SnapKit),这都毫无悬念。但是 JSON-Model 转换并没有找到合适的,没有一个能完全取代 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel)。 +```swift +struct TestExCodable: ExAutoCodable { + @ExCodable + var int: Int = 0 + @ExCodable("string", "s", "nested.nested.string") + var string: String? = nil +} + +``` + +上面代码虽少,但足可以体现 ExCodable 的强大。 + +主要特性: + +- ExCodable 是对 Swift 内置的 `Codable` 的扩展,因此可以享受到诸多便利,比如与 `NSCoding`、[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)、[GenericJSON](https://github.com/iwill/generic-json-swift)、[Alamofire](https://github.com/Alamofire/Alamofire) 等都能无缝对接 +- 在属性上添加注解(`@propertyWrapper`)绑定 JSON key,非常简单、直观,JSON key 与属性同名时,可以简写为 `@ExCodable` +- 支持多个候选 JSON key,依次解析 +- 使用 `.` 连接多层嵌套的 JSON key +- 需要手动 encode/decode 时,可以使用便捷的 subscript 语法 +- 支持常见的数据类型转换,以及灵活的自定义转换 +- 支持更灵活的异常处理,出错时默认返回 `nil`,也支持设置抛出 `error` +- 支持 `struct`、`enum`、`class` 以及子类 +- 支持 `Data`、`String`、`Array`、`Dictionary` 等 JSON 类型 +- 支持类型推断 +- 声明实现 `ExAutoCodable` 协议,即可自动获得 `Codable` 方法的默认实现,无需对属性逐个 encode/decode +- 支持 JSON、PList 以及自定义 encoder/decoder,默认使用 JSON +- 非常轻量 -### Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐; +## 使用方法 -最基本的使用其实还是很便利的: +### 1、ExCodable + +目前,使用 ExCodable 定义属性需要使用 `var`(可以使用 `private(set)` 避免属性被意外修改),并且要提供默认值。 ```swift -struct TestAutoCodable: Codable { - private(set) var int: Int = 0 // `int` 一次 - private(set) var string: String? +struct TestStruct: ExAutoCodable { + @ExCodable private(set) + var int: Int = 0 + @ExCodable("string") private(set) + var string: String? = nil } ``` -但是,一旦不得不手动 Encode/Decode 就完蛋了,相同字段要出现 5 次,而且还要夹杂很多其它代码: +### 2、支持多个候选 JSON key ```swift -struct TestManualCodable: Codable { - - private(set) var int: Int = 0 // `int` 一次 - private(set) var string: String? - - enum Keys: CodingKey { - case int, i // `int` 两次,这里省掉,在第三次、第五次的那里直接写字符串?不要! - case nested, string - } +struct TestAlternativeKeys: ExAutoCodable { + @ExCodable("string", "str", "s") private(set) + var string: String! = nil +} + +``` + +### 3、多层嵌套的 JSON 数据 + +```swift +struct TestNestedKeys: ExAutoCodable { + @ExCodable("nested.nested.string") private(set) + var string: String! = nil +} + +``` + +### 4、自动支持 `RawRepresentable` 类型的 `enum` + +```swift +enum TestEnum: Int, Codable { + case zero = 0, one = 1 +} + +struct TestStructWithEnum: ExAutoCodable { + @ExCodable private(set) + var `enum` = TestEnum.zero +} + +``` + +支持非 `RawRepresentable` 类型的 `enum`,需要自定义 encode/decode。 + +### 5、自定义 Encode/Decode + +`@ExCodable` 支持自定义 `encode` 和 `decode`,并且 `encoder`、`decoder` 支持使用 subscript 方式访问。 + +```swift +struct TestManualEncodeDecode: ExAutoCodable { + @ExCodable("int", encode: { encoder, value in + encoder["int"] = value <= 0 ? 0 : value + }, decode: { decoder in + if let int: Int = decoder["int"], int > 0 { + return int + } + return 0 + }) private(set) + var int: Int = 0 +} + +``` + +### 6、自动类型转换 + +ExCodable 支持灵活的类型转换,兼容多层嵌套的 `Optional` 类型,例如 `Int???`。并且这些转换同样适用于 `RawRepresentable` 类型的属性,自动转换到 `RawValue` 类型并调用 `init(rawValue:)`。 + +A、内置支持的类型转换: + +- boolean: + - `Bool` + - `Int`, `Int8`, `Int16`, `Int32`, `Int64` + - `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` + - `String` +- integer: + - `Bool` + - `Int`, `Int8`, `Int16`, `Int32`, `Int64` + - `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` + - `Double`, `Float` + - `String` +- float: + - `Int`, `Int8`, `Int16`, `Int32`, `Int64` + - `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` + - `Double`, `Float` + - `String` +- string: + - `Bool` + - `Int`, `Int8`, `Int16`, `Int32`, `Int64` + - `UInt`, `UInt8`, `UInt16`, `UInt32`, `UInt64` + - `Double`, `Float` + - `String` + +B、针对单个属性的自定义类型转换: + +```swift +struct TestCustomEncodeDecode: ExAutoCodable { + @ExCodable("int", decode: { decoder in + if let string: String = decoder["string"], + let int = Int(string) { + return int + } + return 0 + }) private(set) + var int: Int = 0 +} + +``` + +C、针对某个 model 的自定义类型转换: + +```swift +struct TestCustomTypeConverter: ExAutoCodable { + @ExCodable("doubleFromBool") private(set) + var doubleFromBool: Double? = nil + @ExCodable("floatFromBool") private(set) + var floatFromBool: Double? = nil +} + +extension TestCustomTypeConverter: ExCodableDecodingTypeConverter { - init(from decoder: Decoder) throws { - if let container = try? decoder.container(keyedBy: Keys.self) { - if let int = (try? container.decodeIfPresent(Int.self, forKey: Keys.int)) // `int` 三次 - ?? (try? container.decodeIfPresent(Int.self, forKey: Keys.i)) { - self.int = int // `int` 四次 + public static func decode(_ container: KeyedDecodingContainer, codingKey: K, as type: T.Type) -> T? { + + // for nested optionals, e.g. `var int: Int??? = nil` + let wrappedType = T?.wrappedType + + // decode Double from Bool + if type is Double.Type || wrappedType is Double.Type { + if let bool = try? container.decodeIfPresent(Bool.self, forKey: codingKey) { + return (bool ? 1.0 : 0.0) as? T } - if let nestedContainer = try? container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested), - let string = try? nestedContainer.decodeIfPresent(String.self, forKey: Keys.string) { - self.string = string + } + // decode Float from Bool + else if type is Float.Type || wrappedType is Float.Type { + if let bool = try? container.decodeIfPresent(Bool.self, forKey: codingKey) { + return (bool ? 1.0 : 0.0) as? T } } + + return nil } +} + +``` + +D、全局的自定义类型转换: + +```swift +struct TestCustomGlobalTypeConverter: ExAutoCodable, Equatable { + @ExCodable("boolFromDouble") private(set) + var boolFromDouble: Bool? = nil +} + +extension ExCodableGlobalDecodingTypeConverter: ExCodableDecodingTypeConverter { - func encode(to encoder: Encoder) throws { - var container = encoder.container(keyedBy: Keys.self) - try? container.encodeIfPresent(int, forKey: Keys.int) // `int` 五次 - var nestedContainer = container.nestedContainer(keyedBy: Keys.self, forKey: Keys.nested) - try? nestedContainer.encodeIfPresent(string, forKey: Keys.string) + public static func decode(_ container: KeyedDecodingContainer<_K>, codingKey: _K, as type: T.Type) -> T? { + + // for nested optionals, e.g. `var int: Int??? = nil` + let wrappedType = T?.wrappedType + + // decode Bool from Double + if type is Bool.Type || wrappedType is Bool.Type { + if let double = try? container.decodeIfPresent(Double.self, forKey: codingKey) { + return (double != 0) as? T + } + } + + return nil } } ``` -### [Codextended](https://github.com/JohnSundell/Codextended) 对 `Codable` 做了大量的简化,但还是要逐个属性 Encode/Decode: +### 7、使用 Subscript 手动 Encode/Decode + +对于一个未使用 `ExCodable` 的类型: ```swift -struct TestCodextended: Codable { - - let int: Int // `int` 一次 - let string: String? +struct TestManualEncodingDecoding { + let int: Int + let string: String +} + +``` + +使用 Subscript 手动 encode/decode,要比 Swift 原生语法简单得多: + +```swift +extension TestManualEncodingDecoding: Codable { - /* Codextended 给的例子没有这个,而是在 `init` 和 `decode` - 方法里分别写两个 `"int"`、两个 `"string"`,但那是不对的,代码 - 很多时可能会改了一个忘掉另一个 */ enum Keys: CodingKey { - case int, i // `int` 两次 - case string + case int, string } init(from decoder: Decoder) throws { - int = try decoder.decodeIfPresent(Keys.int) // `int` 三次 - ?? try decoder.decodeIfPresent(Keys.i) - ?? 0 - // !!!: Codextended 目前不支持 Nested-Keys - string = try decoder.decodeIfPresent(Keys.string) + int = decoder[Keys.int] ?? 0 + string = decoder[Keys.string] ?? "" } - func encode(to encoder: Encoder) throws { - try encoder.encode(int, for: "int") // `int` 四次 - try encoder.encode(string, for: "string") + encoder[Keys.int] = int + encoder[Keys.string] = string } } ``` -### 另外 GitHub 上 Star 比较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等: +### 8、支持更灵活的异常处理 -他们都各自构建了整套的序列化、反序列化机制,各有所长,但是相比 Objective-C 的 [YYModel](https://github.com/ibireme/YYModel) 多少都欠了点意思。与 `Codable` 不兼容、代码很复杂不说,甚至还有直接读写内存的 —— “依赖于从 Swift Runtime 源码中推断的内存规则,任何变动我们将随时跟进”,这就不大靠谱了,至少不够优雅。 +ExCodable 会默认忽略 JSON-Model 转换时遇到的 `EncodingError.invalidValue`、`DecodingError.keyNotFound`、`DecodingError.valueNotFound` 和 `DecodingError.typeMismatch` 等错误,出错的属性使用默认值。只有 JSON 数据本身有问题时才会抛出错误。 -## ExCodable +ExCodable 也支持抛出异常: -调研一番之后倾向于用 Codextended,起初有考虑直接基于它做扩展来实现 Key-Mapping,但是受到限制较多,只能自己动手了。 +- 设置 `nonnull: true` 允许抛出 `EncodingError.invalidValue`、`DecodingError.keyNotFound` 和 `DecodingError.valueNotFound` +- 设置 `throws: true` 允许抛出 `DecodingError.typeMismatch` -Codextended 最欠缺的是 Key-Mapping,经过各种摸索、尝试,确定 `KeyPath` 方式可行。解决掉关键问题后面就简单了,很快差不多实现了 YYModel 的所有特性;同时借鉴了 Codextended,重新写了关键部分的实现,有些调整、也有些舍弃。 +```swift +struct TestNonnullAndThrows: ExAutoCodable { + @ExCodable("int", nonnull: true, throws: true) private(set) + var int: Int! = 0 +} -于是便有了 `ExCodable`! +``` -### 上面场景,用 `ExCodable` 就简单多了: +### 9、`class` 以及子类 ```swift -struct TestExCodable: ExCodable, Equatable { - - private(set) var int: Int = 0 // `int` 一次 - private(set) var string: String? - - static var keyMapping: [KeyMap] = [ - KeyMap(\.int, to: "int", "i"), // `int` 两次 - KeyMap(\.string, to: "nested.string") - ] - - init(from decoder: Decoder) throws { - decode(from: decoder, with: Self.keyMapping) +class TestClass: ExAutoCodable { + @ExCodable private(set) + var int: Int = 0 + @ExCodable private(set) + var string: String? = nil + required init() {} + init(int: Int, string: String?) { + (self.int, self.string) = (int, string) } } ``` -### `ExCodable` 用法解析: - -定义 `struct`,使用 `var` 声明变量、并设置默认值,可以使用 `private(set)` 来防止属性被外部修改; - -> `Optional` 类型不需要默认值; -> 想用 `let` 也不是不可以,参考 [Usage](https://github.com/ExCodable/ExCodable#usage); - ```swift -struct TestStruct: Equatable { - private(set) var int: Int = 0 - private(set) var string: String? +class TestSubclass: TestClass { + @ExCodable private(set) + var bool: Bool = false + required init() { super.init() } + required init(int: Int, string: String, bool: Bool) { + self.bool = bool + super.init(int: int, string: string) + } } ``` -实现 `ExCodable` 协议,通过 `keyMapping` 设置 `KeyPath` 到 JSON-Key 的映射,`init` 和 `encode` 方法都只要一行代码; - -> 当 `encode` 方法只需这一行代码时它也是可以省略的,`ExCodable` 提供了默认实现。但是受 Swift 对初始化过程的严格限制,`init` 方法不能省略。 +### 10、支持类型推断 ```swift -extension TestStruct: ExCodable { - static var keyMapping: [KeyMap] = [ - KeyMap(\.int, to: "int", "i"), - KeyMap(\.string, to: "nested.string") - ] - init(from decoder: Decoder) throws { - decode(from: decoder, with: Self.keyMapping) - // 特殊逻辑同样可以在这里手动解决 - // 比如 `string = (int == 0) ? decoder["a"] : decoder["b"]` - } - // func encode(to encoder: Encoder) throws { - // encode(to: encoder, with: Self.keyMapping) - // // 特殊逻辑同样可以在这里手动解决 - // // 比如 `encoder["string"] = (string == "") ? nil : string` - // } +struct TestStruct: ExAutoCodable, Equatable { + @ExCodable("int") private(set) + var int: Int = 0 + @ExCodable("string") private(set) + var string: String? = nil } +let json = Data(#"{"int":200,"string":"OK"}"#.utf8) +let model = try? TestStruct.decoded(from: json) + +let dict = try? model.encoded() as [String: Any] +let copy = try? dict.decoded() as TestStruct + ``` -序列化、反序列化也很方便; +## 安装 + +Swift Package Manager: ```swift -let test = TestStruct(int: 100, string: "Continue") -let data = test.encoded() as Data? // Model encode to JSON +.package(url: "https://github.com/ExCodable/ExCodable", from: "1.0.0") + +``` -let copy1 = data?.decoded() as TestStruct? // decode JSON to Model -let copy2 = TestStruct.decoded(from: data) // or Model decode from JSON +CocoaPods: -XCTAssertEqual(copy1, test) -XCTAssertEqual(copy2, test) +```ruby +pod 'ExCodable', '~> 1.0.0' ``` -更多示例可参考 [Usage](https://github.com/ExCodable/ExCodable#usage) 以及单元测试代码。 +## 升级 -### 将下面代码片段添加到 Xcode,只要记住 `ExCodable` 就可以了: +如果你用过 0.x 版本,非常感谢!但是时候升级了,升级到 1.0 版本有两种方式。 -> Language: Swift -> Platform: All -> Completion: ExCodable -> Availability: Top Level +A、继续使用废弃的 API —— 快速、工作量小: + +- 全局搜索 `ExCodable`,替换成 `ExCodableDEPRECATED` +- 如果你实现了 `KeyedDecodingContainerCustomTypeConversion` 的 `decodeForTypeConversion(_:codingKey:as:)` 方法,在前面添加一个 `static` ```swift -<#extension/struct/class#> <#Type#>: ExCodable { - static var <#keyMapping#>: [KeyMap<<#SelfType#>>] = [ - KeyMap(\.<#property#>, to: <#"key"#>), - <#...#> +struct TestExCodable { + private(set) var int: Int = 0 + private(set) var string: String? +} + +extension TestExCodable: ExCodableDEPRECATED { + static let keyMapping: [KeyMap] = [ + KeyMap(\.int, to: "int"), + KeyMap(\.string, to: "nested.nested.string", "string", "str", "s") ] init(from decoder: Decoder) throws { - decode<#Reference#>(from: decoder, with: Self.<#keyMapping#>) - } - func encode(to encoder: Encoder) throws { - encode(to: encoder, with: Self.<#keyMapping#>) + try decode(from: decoder, with: Self.keyMapping) } } ``` +B、升级: + +- 使用 `ExAutoCodable` 协议替代 `ExCodable` +- 删除 `init(from decoder: Decoder) throws` 方法 +- 删除 `keyMapping` 静态属性 +- 改用 `@ExCodable("", "", ...)` 绑定 JSON key +- 具体参考上面 [使用方法](#使用方法)、以及代码中的单元测试 + +## 未来 + +Swift 5.9 终于引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),并且非常及时地出现了基于 Macros 的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最好的实现方式。这让我一度想放弃维护 ExCodable。但是仔细看了一下,调用方式我还是喜欢 ExCodable。 + +未来 ExCodable 也考虑使用 Macros 实现,在保持目前良好特性的同时,突破 Swift 语法对目前方案的各种限制,敬请期待 —— 不确定多久 🫣 + ## 感谢 -在此,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性,他们给了我极大的启发。 +在此,再次,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性给我的极大的启发。 + +如果你喜欢 ExCodable,欢迎 [给个星星](https://github.com/ExCodable/ExCodable/#repository-container-header) ⭐️ 🤩 -`ExCodable` 还是一个崭新的框架,使用中遇到任何问题欢迎反馈:[i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 +使用中遇到任何问题,欢迎 [反馈](https://github.com/iwill/ExCodable/issues/new) / [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 From e368df1572a1996187c7543e9cdd72d8e79c005b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=ADng?= Date: Mon, 5 Aug 2024 22:17:54 +0800 Subject: [PATCH 47/54] Create CNAME --- CNAME | 1 + 1 file changed, 1 insertion(+) create mode 100644 CNAME diff --git a/CNAME b/CNAME new file mode 100644 index 0000000..5e0cc3d --- /dev/null +++ b/CNAME @@ -0,0 +1 @@ +excodable.iwill.im \ No newline at end of file From 484832f6fcc16be2824a43c7a07e0e371b6fb73e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=ADng?= Date: Mon, 5 Aug 2024 23:05:54 +0800 Subject: [PATCH 48/54] Update CNAME --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index 5e0cc3d..48b2d81 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -excodable.iwill.im \ No newline at end of file +exc.iwill.im \ No newline at end of file From 3f39aa8d00207a05d2fdb257f2cdfec8b9103114 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=C3=ADng?= Date: Tue, 6 Aug 2024 01:03:09 +0800 Subject: [PATCH 49/54] Update CNAME --- CNAME | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CNAME b/CNAME index 48b2d81..5e0cc3d 100644 --- a/CNAME +++ b/CNAME @@ -1 +1 @@ -exc.iwill.im \ No newline at end of file +excodable.iwill.im \ No newline at end of file From d3adbbac6ca57dfdc18dbcad5a0ab0513ccf37c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Tue, 6 Aug 2024 12:02:53 +0800 Subject: [PATCH 50/54] opt: docs --- 0.x.md | 10 ++++-- index.md | 95 ++++++++++++++++++++++++++++++++++---------------------- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/0.x.md b/0.x.md index 192eaf0..7a8f6a1 100644 --- a/0.x.md +++ b/0.x.md @@ -1,4 +1,10 @@ -ExCodable +--- +layout: default +title: ExCodable +description: KeyMapping Extensions for Swift Codable +--- + +ExCodable 0.x ======== [![ExCodable](https://iwill.im/images/ExCodable-1920x500.png)](https://github.com/ExCodable/ExCodable#readme) @@ -19,7 +25,7 @@ ExCodable -------- -> - **1.x**: New Released +> - [**1.x**: Recently Released](./index.md) > - 0.x: DEPRECATED ## [ExCodable](https://github.com/ExCodable/ExCodable) 是我在春节期间、带娃之余、用了几个晚上完成的一个 Swift 版的 JSON-Model 转换工具。 diff --git a/index.md b/index.md index 76dd9c7..cb8bcac 100644 --- a/index.md +++ b/index.md @@ -1,4 +1,4 @@ -ExCodable +ExCodable 1.0 ======== [![ExCodable](https://iwill.im/images/ExCodable-1.x-1920x500.png)](https://github.com/ExCodable/ExCodable#readme) @@ -21,9 +21,9 @@ ExCodable 是一个 Swift 版 JSON-Model 转换工具,现在迎来重要升级 「若非必要,勿造轮子」。但显然,我又造了一个,所以它一定是必要的: -- Swift 内置的 `Codable` 可以满足刚需,但也有官方框架的通病 —— 繁琐 +- Swift 内置的 `Codable` 能用,但也有官方框架的通病 —— 繁琐 - [Codextended](https://github.com/JohnSundell/Codextended) 想法特别好,对 `Codable` 做了大量的简化,但还是要逐个属性 encode/decode -- 其它 star 较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等(不知道最近两年有没有新的),不兼容 `Codable`,语法奇怪、繁琐,甚至还有读写内存的,说实话 —— 丑得要死! +- 其它 star 较多的 [ObjectMapper](https://github.com/tristanhimmelman/ObjectMapper)、[HandyJSON](https://github.com/alibaba/HandyJSON)、[KakaJSON](https://github.com/kakaopensource/KakaJSON) 等等(不知道最近两年有没有新的),都不兼容 `Codable`,语法奇怪、繁琐,甚至还有读写内存的,说实话 —— 丑得要死! ObjC 时代,最好的 JSON-Model 转换非 [YYModel](https://github.com/ibireme/YYModel) 莫属,可惜没有 Swift 版,所以 [自己写一个吧](./0.x.md/#excodable-是我在春节期间带娃之余用了几个晚上完成的一个-swift-版的-json-model-转换工具)。 @@ -46,12 +46,12 @@ struct TestExCodable: ExAutoCodable { - ExCodable 是对 Swift 内置的 `Codable` 的扩展,因此可以享受到诸多便利,比如与 `NSCoding`、[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)、[GenericJSON](https://github.com/iwill/generic-json-swift)、[Alamofire](https://github.com/Alamofire/Alamofire) 等都能无缝对接 - 在属性上添加注解(`@propertyWrapper`)绑定 JSON key,非常简单、直观,JSON key 与属性同名时,可以简写为 `@ExCodable` - 支持多个候选 JSON key,依次解析 -- 使用 `.` 连接多层嵌套的 JSON key +- 使用 `.` 拼接多层嵌套的 JSON key - 需要手动 encode/decode 时,可以使用便捷的 subscript 语法 - 支持常见的数据类型转换,以及灵活的自定义转换 -- 支持更灵活的异常处理,出错时默认返回 `nil`,也支持设置抛出 `error` +- 灵活的异常处理,出错时默认返回 `nil`,也支持设置抛出 `error` - 支持 `struct`、`enum`、`class` 以及子类 -- 支持 `Data`、`String`、`Array`、`Dictionary` 等 JSON 类型 +- 支持 `Data`、`String`、`Array`、`Dictionary` 等 JSON 格式 - 支持类型推断 - 声明实现 `ExAutoCodable` 协议,即可自动获得 `Codable` 方法的默认实现,无需对属性逐个 encode/decode - 支持 JSON、PList 以及自定义 encoder/decoder,默认使用 JSON @@ -73,7 +73,7 @@ struct TestStruct: ExAutoCodable { ``` -### 2、支持多个候选 JSON key +### 2、多个候选 JSON key ```swift struct TestAlternativeKeys: ExAutoCodable { @@ -83,7 +83,7 @@ struct TestAlternativeKeys: ExAutoCodable { ``` -### 3、多层嵌套的 JSON 数据 +### 3、多层嵌套的 JSON key ```swift struct TestNestedKeys: ExAutoCodable { @@ -93,7 +93,7 @@ struct TestNestedKeys: ExAutoCodable { ``` -### 4、自动支持 `RawRepresentable` 类型的 `enum` +### 4、`RawRepresentable` 类型的 `enum` ```swift enum TestEnum: Int, Codable { @@ -107,7 +107,7 @@ struct TestStructWithEnum: ExAutoCodable { ``` -支持非 `RawRepresentable` 类型的 `enum`,需要自定义 encode/decode。 +非 `RawRepresentable` 类型的 `enum` 需要自定义的 encode/decode。 ### 5、自定义 Encode/Decode @@ -115,7 +115,7 @@ struct TestStructWithEnum: ExAutoCodable { ```swift struct TestManualEncodeDecode: ExAutoCodable { - @ExCodable("int", encode: { encoder, value in + @ExCodable(encode: { encoder, value in encoder["int"] = value <= 0 ? 0 : value }, decode: { decoder in if let int: Int = decoder["int"], int > 0 { @@ -157,14 +157,13 @@ A、内置支持的类型转换: - `Double`, `Float` - `String` -B、针对单个属性的自定义类型转换: +B、单个属性的自定义类型转换: ```swift struct TestCustomEncodeDecode: ExAutoCodable { - @ExCodable("int", decode: { decoder in - if let string: String = decoder["string"], - let int = Int(string) { - return int + @ExCodable(decode: { decoder in + if let string: String = decoder["string"] { + return string.count } return 0 }) private(set) @@ -173,13 +172,13 @@ struct TestCustomEncodeDecode: ExAutoCodable { ``` -C、针对某个 model 的自定义类型转换: +C、model 中的自定义类型转换: ```swift struct TestCustomTypeConverter: ExAutoCodable { - @ExCodable("doubleFromBool") private(set) + @ExCodable private(set) var doubleFromBool: Double? = nil - @ExCodable("floatFromBool") private(set) + @ExCodable private(set) var floatFromBool: Double? = nil } @@ -213,7 +212,7 @@ D、全局的自定义类型转换: ```swift struct TestCustomGlobalTypeConverter: ExAutoCodable, Equatable { - @ExCodable("boolFromDouble") private(set) + @ExCodable private(set) var boolFromDouble: Bool? = nil } @@ -249,7 +248,7 @@ struct TestManualEncodingDecoding { ``` -使用 Subscript 手动 encode/decode,要比 Swift 原生语法简单得多: +使用 subscript 手动 encode/decode,要比 Swift 原生语法简单得多: ```swift extension TestManualEncodingDecoding: Codable { @@ -270,18 +269,18 @@ extension TestManualEncodingDecoding: Codable { ``` -### 8、支持更灵活的异常处理 +### 8、更灵活的异常处理 -ExCodable 会默认忽略 JSON-Model 转换时遇到的 `EncodingError.invalidValue`、`DecodingError.keyNotFound`、`DecodingError.valueNotFound` 和 `DecodingError.typeMismatch` 等错误,出错的属性使用默认值。只有 JSON 数据本身有问题时才会抛出错误。 +ExCodable 默认忽略 JSON-Model 转换时遇到的 `EncodingError.invalidValue`、`DecodingError.keyNotFound`、`DecodingError.valueNotFound` 和 `DecodingError.typeMismatch` 等错误,出错的属性不处理 —— encode 时跳过、decode 时保持默认值。只有 JSON 数据本身有问题时才会抛出错误。 -ExCodable 也支持抛出异常: +ExCodable 也支持出错时终止转换,抛出错误: - 设置 `nonnull: true` 允许抛出 `EncodingError.invalidValue`、`DecodingError.keyNotFound` 和 `DecodingError.valueNotFound` - 设置 `throws: true` 允许抛出 `DecodingError.typeMismatch` ```swift struct TestNonnullAndThrows: ExAutoCodable { - @ExCodable("int", nonnull: true, throws: true) private(set) + @ExCodable(nonnull: true, throws: true) private(set) var int: Int! = 0 } @@ -291,10 +290,12 @@ struct TestNonnullAndThrows: ExAutoCodable { ```swift class TestClass: ExAutoCodable { + @ExCodable private(set) var int: Int = 0 @ExCodable private(set) var string: String? = nil + required init() {} init(int: Int, string: String?) { (self.int, self.string) = (int, string) @@ -305,8 +306,10 @@ class TestClass: ExAutoCodable { ```swift class TestSubclass: TestClass { + @ExCodable private(set) var bool: Bool = false + required init() { super.init() } required init(int: Int, string: String, bool: Bool) { self.bool = bool @@ -316,19 +319,21 @@ class TestSubclass: TestClass { ``` -### 10、支持类型推断 +### 10、类型推断 ```swift struct TestStruct: ExAutoCodable, Equatable { - @ExCodable("int") private(set) + @ExCodable private(set) var int: Int = 0 - @ExCodable("string") private(set) + @ExCodable private(set) var string: String? = nil } +// 正常的转换 let json = Data(#"{"int":200,"string":"OK"}"#.utf8) let model = try? TestStruct.decoded(from: json) +// 类型推断 let dict = try? model.encoded() as [String: Any] let copy = try? dict.decoded() as TestStruct @@ -340,21 +345,19 @@ Swift Package Manager: ```swift .package(url: "https://github.com/ExCodable/ExCodable", from: "1.0.0") - ``` CocoaPods: ```ruby pod 'ExCodable', '~> 1.0.0' - ``` ## 升级 -如果你用过 0.x 版本,非常感谢!但是时候升级了,升级到 1.0 版本有两种方式。 +如果你用过 0.x 版本,感谢支持!但是时候升级了,ExCodable 依然保留了旧的 API,从而降低升级的难度。 -A、继续使用废弃的 API —— 快速、工作量小: +首先,升级到 1.0 之后可以继续使用废弃的 API —— 快速、工作量小: - 全局搜索 `ExCodable`,替换成 `ExCodableDEPRECATED` - 如果你实现了 `KeyedDecodingContainerCustomTypeConversion` 的 `decodeForTypeConversion(_:codingKey:as:)` 方法,在前面添加一个 `static` @@ -377,7 +380,7 @@ extension TestExCodable: ExCodableDEPRECATED { ``` -B、升级: +然后逐步升级到新的语法: - 使用 `ExAutoCodable` 协议替代 `ExCodable` - 删除 `init(from decoder: Decoder) throws` 方法 @@ -385,16 +388,34 @@ B、升级: - 改用 `@ExCodable("", "", ...)` 绑定 JSON key - 具体参考上面 [使用方法](#使用方法)、以及代码中的单元测试 +```swift +struct TestExCodable: ExAutoCodable { + @ExCodable private(set) + var int: Int = 0 + @ExCodable("nested.nested.string", "string", "str", "s") private(set) + var string: String? +} + +``` + ## 未来 -Swift 5.9 终于引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),并且非常及时地出现了基于 Macros 的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最好的实现方式。这让我一度想放弃维护 ExCodable。但是仔细看了一下,调用方式我还是喜欢 ExCodable。 +Swift 5.9 发布时引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),并且非常及时地出现了基于它实现的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最科学的实现方式。这让我一度想放弃维护 ExCodable,但是我还是更喜欢 ExCodable 的使用方式。 + +未来 ExCodable 也考虑使用 Macros 实现,在保持目前良好特性的同时,突破 Swift 语法对目前方案的种种限制,敬请期待 —— 不确定多久 🫣 -未来 ExCodable 也考虑使用 Macros 实现,在保持目前良好特性的同时,突破 Swift 语法对目前方案的各种限制,敬请期待 —— 不确定多久 🫣 +## 星星 + +如果你喜欢 ExCodable,欢迎 [给个星星](https://github.com/ExCodable/ExCodable#repository-container-header) ⭐️ 🤩 ## 感谢 在此,再次,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性给我的极大的启发。 -如果你喜欢 ExCodable,欢迎 [给个星星](https://github.com/ExCodable/ExCodable/#repository-container-header) ⭐️ 🤩 +## 关于 + +我是 [Míng](https://github.com/iwill),使用中遇到任何问题,欢迎 [反馈](https://github.com/ExCodable/ExCodable/issues/new) / [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 + +## 开源 -使用中遇到任何问题,欢迎 [反馈](https://github.com/iwill/ExCodable/issues/new) / [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 +[代码](https://github.com/ExCodable/ExCodable) 在 [MIT](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) 协议下开源。 From 905e7627a85db67b9177b238a9fdd34dbb3d56ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Tue, 6 Aug 2024 12:42:52 +0800 Subject: [PATCH 51/54] opt: doct title and desc --- 0.x.md | 2 +- index.md | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/0.x.md b/0.x.md index 7a8f6a1..4784ef0 100644 --- a/0.x.md +++ b/0.x.md @@ -1,6 +1,6 @@ --- layout: default -title: ExCodable +title: ExCodable 0.x description: KeyMapping Extensions for Swift Codable --- diff --git a/index.md b/index.md index cb8bcac..d8827af 100644 --- a/index.md +++ b/index.md @@ -1,3 +1,8 @@ +--- +layout: default +title: ExCodable +--- + ExCodable 1.0 ======== From 29f1d4e753ec7bb6d9ec6dfe3b047883fa1966a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Tue, 6 Aug 2024 13:13:26 +0800 Subject: [PATCH 52/54] opt: docs --- index.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/index.md b/index.md index d8827af..c7da75c 100644 --- a/index.md +++ b/index.md @@ -49,7 +49,7 @@ struct TestExCodable: ExAutoCodable { 主要特性: - ExCodable 是对 Swift 内置的 `Codable` 的扩展,因此可以享受到诸多便利,比如与 `NSCoding`、[SwiftyJSON](https://github.com/SwiftyJSON/SwiftyJSON)、[GenericJSON](https://github.com/iwill/generic-json-swift)、[Alamofire](https://github.com/Alamofire/Alamofire) 等都能无缝对接 -- 在属性上添加注解(`@propertyWrapper`)绑定 JSON key,非常简单、直观,JSON key 与属性同名时,可以简写为 `@ExCodable` +- 在属性上添加注解(`@propertyWrapper`)绑定 JSON key ,JSON key 与属性同名时,可以简写为 `@ExCodable` —— 目前位置最简单、直观的方式 - 支持多个候选 JSON key,依次解析 - 使用 `.` 拼接多层嵌套的 JSON key - 需要手动 encode/decode 时,可以使用便捷的 subscript 语法 @@ -344,6 +344,8 @@ let copy = try? dict.decoded() as TestStruct ``` +> 更多用法参考代码中的单元测试。 + ## 安装 Swift Package Manager: @@ -391,7 +393,7 @@ extension TestExCodable: ExCodableDEPRECATED { - 删除 `init(from decoder: Decoder) throws` 方法 - 删除 `keyMapping` 静态属性 - 改用 `@ExCodable("", "", ...)` 绑定 JSON key -- 具体参考上面 [使用方法](#使用方法)、以及代码中的单元测试 +- 具体参考上面 [使用方法](#使用方法) ```swift struct TestExCodable: ExAutoCodable { @@ -405,21 +407,21 @@ struct TestExCodable: ExAutoCodable { ## 未来 -Swift 5.9 发布时引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),并且非常及时地出现了基于它实现的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最科学的实现方式。这让我一度想放弃维护 ExCodable,但是我还是更喜欢 ExCodable 的使用方式。 +Swift 5.9 发布时引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),很快我就看到了基于它实现的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最科学的「实现方式」。这让我一度想放弃维护 ExCodable,但是我还是更喜欢 ExCodable 的「使用方式」。 -未来 ExCodable 也考虑使用 Macros 实现,在保持目前良好特性的同时,突破 Swift 语法对目前方案的种种限制,敬请期待 —— 不确定多久 🫣 +计划未来也使用 Macros 实现重写 ExCodable,在保持目前良好特性的同时,突破 Swift 语法对目前方案的种种限制,敬请期待 —— 不确定多久 🫣 ## 星星 如果你喜欢 ExCodable,欢迎 [给个星星](https://github.com/ExCodable/ExCodable#repository-container-header) ⭐️ 🤩 -## 感谢 +## 致敬 -在此,再次,要特别感谢 John Sundell 的 [Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意、以及 ibireme 的 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性给我的极大的启发。 +在此,再次,致敬 John Sundell 和 ibireme,[Codextended](https://github.com/JohnSundell/Codextended) 的非凡创意和 [YYModel](https://github.com/ibireme/YYModel) 的丰富特性给了我的极大的启发! ## 关于 -我是 [Míng](https://github.com/iwill),使用中遇到任何问题,欢迎 [反馈](https://github.com/ExCodable/ExCodable/issues/new) / [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 +我是 [Míng](https://github.com/iwill),使用中遇到任何问题,欢迎 [反馈](https://github.com/ExCodable/ExCodable/issues) / [i+ExCodable@iwill.im](mailto:i+ExCodable@iwill.im)。 ## 开源 From 4774537abc14cd2241bbd9d62aa1fd20d1899940 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Tue, 6 Aug 2024 16:10:05 +0800 Subject: [PATCH 53/54] opt: docs --- 0.x.md | 2 +- index.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/0.x.md b/0.x.md index 4784ef0..ffef636 100644 --- a/0.x.md +++ b/0.x.md @@ -21,7 +21,7 @@ ExCodable 0.x [![LICENSE](https://img.shields.io/github/license/ExCodable/ExCodable.svg)](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) [![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) -[En](https://github.com/ExCodable/ExCodable) | 中文 +[En](https://github.com/ExCodable/ExCodable) \| 中文 -------- diff --git a/index.md b/index.md index c7da75c..cdc28fe 100644 --- a/index.md +++ b/index.md @@ -20,7 +20,7 @@ ExCodable 1.0 [![LICENSE](https://img.shields.io/github/license/ExCodable/ExCodable.svg)](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) [![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) -[En](https://github.com/ExCodable/ExCodable) | 中文 +[En](https://github.com/ExCodable/ExCodable) \| 中文 ExCodable 是一个 Swift 版 JSON-Model 转换工具,现在迎来重要升级,发布 1.0 版本。 @@ -407,7 +407,7 @@ struct TestExCodable: ExAutoCodable { ## 未来 -Swift 5.9 发布时引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),很快我就看到了基于它实现的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最科学的「实现方式」。这让我一度想放弃维护 ExCodable,但是我还是更喜欢 ExCodable 的「使用方式」。 +Swift 5.9 发布时引入了 [Macros](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/macros/),很快我就看到了基于它实现的 [MetaCodable](https://github.com/SwiftyLab/MetaCodable),这是目前最科学的「实现方式」。这让我一度想放弃维护 ExCodable,但我还是更喜欢 ExCodable 灵活的[「使用方式」](#使用方法)。 计划未来也使用 Macros 实现重写 ExCodable,在保持目前良好特性的同时,突破 Swift 语法对目前方案的种种限制,敬请期待 —— 不确定多久 🫣 From a6753830e3ac3fa7f6b7cc13f3e19fd6a9961ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mi=CC=81ng?= Date: Fri, 6 Sep 2024 21:19:30 +0800 Subject: [PATCH 54/54] add bagdes --- 0.x.md | 1 + index.md | 3 +++ 2 files changed, 4 insertions(+) diff --git a/0.x.md b/0.x.md index ffef636..3964e15 100644 --- a/0.x.md +++ b/0.x.md @@ -19,6 +19,7 @@ ExCodable 0.x [![Cocoapods](https://img.shields.io/cocoapods/v/ExCodable.svg)](https://cocoapods.org/pods/ExCodable)
[![LICENSE](https://img.shields.io/github/license/ExCodable/ExCodable.svg)](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) +[![GitHub stars](https://badgen.net/github/stars/ExCodable/ExCodable)](https://github.com/ExCodable/ExCodable/stargazers/) [![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) [En](https://github.com/ExCodable/ExCodable) \| 中文 diff --git a/index.md b/index.md index cdc28fe..3783751 100644 --- a/index.md +++ b/index.md @@ -18,10 +18,13 @@ ExCodable 1.0 [![Cocoapods](https://img.shields.io/cocoapods/v/ExCodable.svg)](https://cocoapods.org/pods/ExCodable)
[![LICENSE](https://img.shields.io/github/license/ExCodable/ExCodable.svg)](https://github.com/ExCodable/ExCodable/blob/master/LICENSE) +[![GitHub stars](https://badgen.net/github/stars/ExCodable/ExCodable)](https://github.com/ExCodable/ExCodable/stargazers/) [![@minglq](https://img.shields.io/twitter/url?url=https%3A%2F%2Fgithub.com%2Fiwill%2FExCodable)](https://twitter.com/minglq) [En](https://github.com/ExCodable/ExCodable) \| 中文 +-------- + ExCodable 是一个 Swift 版 JSON-Model 转换工具,现在迎来重要升级,发布 1.0 版本。 「若非必要,勿造轮子」。但显然,我又造了一个,所以它一定是必要的: