使用宏来增强 Swift
的 Codable
实现。
MetaCodable
框架暴露了自定义宏,可用于生成动态 Codable
实现。该框架的核心是 Codable()
宏,它在其他宏提供的数据的辅助下生成实现。
MetaCodable
旨在通过提供以下内置功能来增强您的 Codable
实现
CodedAt(_:)
声明自定义 CodingKey
值,而无需您编写所有 CodingKey
值。CodedAt(_:)
和 CodedIn(_:)
为嵌套的 CodingKey
值创建扁平化的模型。CodedAt(_:)
(不带参数) 创建多个 Codable
类型的组合。CodedAs(_:_:)
提供的额外备用 CodingKey
读取数据。Default(_:)
提供默认值,或者仅在缺少值时使用 Default(ifMissing:)
提供默认值。不同的默认值也可以分别用于值缺失和其他错误,使用 Default(ifMissing:forErrors:)
。HelperCoder
创建自定义解码/编码策略,并将其与 CodedBy(_:)
、CodedBy(_:properties:)
或其他一起使用。 例如,LossySequenceCoder
等。CodedAs(_:_:)
指定不同的 case 值,并允许 case 值/协议类型标识符类型与 String
不同,使用 CodedAs()
。CodedAt(_:)
指定枚举 case/协议类型标识符路径,并使用 ContentAt(_:_:)
指定 case 内容路径。UnTagged()
。IgnoreCoding()
、IgnoreDecoding()
和 IgnoreEncoding()
从解码/编码中忽略特定属性/case。允许基于自定义条件使用 IgnoreEncoding(if:)
忽略编码。CodingKeys(_:)
的不同大小写风格的键。IgnoreCodingInitialized()
从解码/编码中忽略类型/case 的所有初始化属性,除非明确要求通过附加任何编码属性(例如 CodedIn(_:)
、CodedAt(_:)
、CodedBy(_:)
、Default(_:)
等)进行解码/编码。MetaProtocolCodable
构建工具插件从 DynamicCodable
类型生成协议解码/编码 HelperCoder
。查看此宏的限制.
平台 | 最低 Swift 版本 | 安装 | 状态 |
---|---|---|---|
iOS 13.0+ / macOS 10.15+ / tvOS 13.0+ / watchOS 6.0+ | 5.9 | Swift Package Manager, CocoaPods | 完全测试 |
Linux | 5.9 | Swift Package Manager | 完全测试 |
Windows | 5.9.1 | Swift Package Manager | 完全测试 |
Swift Package Manager 是一种用于自动化 Swift 代码分发的工具,已集成到 swift
编译器中。
设置好 Swift 包后,将 MetaCodable
作为依赖项添加到 Package.swift
的 dependencies
值中非常简单。
.package(url: "https://github.com/SwiftyLab/MetaCodable.git", from: "1.0.0"),
然后,通过将其添加到 target
的 dependencies
值中,可以将 MetaCodable
模块产品作为依赖项添加到您选择的 target
中。
.product(name: "MetaCodable", package: "MetaCodable"),
CocoaPods 是 Cocoa 项目的依赖项管理器。有关使用和安装说明,请访问他们的网站。 要使用 CocoaPods 将 MetaCodable
集成到您的 Xcode 项目中,请在您的 Podfile
中指定它
pod 'MetaCodable'
MetaCodable
允许摆脱某些典型的 Codable
实现中经常需要的样板代码,并具有以下功能
例如,在官方 文档 中,要为 Landmark
类型的 2 个字段定义自定义 CodingKey
,您必须编写
struct Landmark: Codable {
var name: String
var foundingYear: Int
var location: Coordinate
var vantagePoints: [Coordinate]
enum CodingKeys: String, CodingKey {
case name = "title"
case foundingYear = "founding_date"
case location
case vantagePoints
}
}
但是使用 MetaCodable
,您只需要编写以下内容
@Codable
struct Landmark {
@CodedAt("title")
var name: String
@CodedAt("founding_date")
var foundingYear: Int
var location: Coordinate
var vantagePoints: [Coordinate]
}
例如,在官方 文档 中,要解码像这样的 JSON
{
"latitude": 0,
"longitude": 0,
"additionalInfo": {
"elevation": 0
}
}
你必须编写所有这些样板代码
struct Coordinate {
var latitude: Double
var longitude: Double
var elevation: Double
enum CodingKeys: String, CodingKey {
case latitude
case longitude
case additionalInfo
}
enum AdditionalInfoKeys: String, CodingKey {
case elevation
}
}
extension Coordinate: Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
latitude = try values.decode(Double.self, forKey: .latitude)
longitude = try values.decode(Double.self, forKey: .longitude)
let additionalInfo = try values.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
elevation = try additionalInfo.decode(Double.self, forKey: .elevation)
}
}
extension Coordinate: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(latitude, forKey: .latitude)
try container.encode(longitude, forKey: .longitude)
var additionalInfo = container.nestedContainer(keyedBy: AdditionalInfoKeys.self, forKey: .additionalInfo)
try additionalInfo.encode(elevation, forKey: .elevation)
}
}
但是使用 MetaCodable
,您只需要编写以下内容
@Codable
struct Coordinate {
var latitude: Double
var longitude: Double
@CodedAt("additionalInfo", "elevation")
var elevation: Double
}
您甚至可以使用 CodedIn
宏进一步最小化,因为最终的 CodingKey
值与字段名称相同
@Codable
struct Coordinate {
var latitude: Double
var longitude: Double
@CodedIn("additionalInfo")
var elevation: Double
}
您可以提供一个默认值,而不是在缺少数据或类型不匹配时抛出错误,该默认值将在这种情况下分配。 以下使用 MetaCodable
的定义
@Codable
struct CodableData {
@Default("some")
let field: String
}
当提供空的 JSON ({}
) 或具有类型不匹配的 JSON ({ "field": 5 }
) 时,将不会抛出任何错误。 在这种情况下,将分配默认值。
此外,可以生成成员初始化程序,该程序使用此默认值作为该字段。
@Codable
@MemberInit
struct CodableData {
@Default("some")
let field: String
}
生成的成员初始化程序将如下所示
init(field: String = "some") {
self.field = field
}
该库提供了以下助手,可满足常见的自定义解码/编码需求
LossySequenceCoder
仅解码序列中的有效数据,而忽略无效数据,而不是以传统方式完全失败解码。ValueCoder
即使 Bool
、Int
、Double
、String
等基本类型以其他类型表示,也可以解码这些基本类型,例如从 "1"
解码 Int
,从 "yes"
解码布尔值等。Since1970DateCoder
) 或日期格式化程序 (DateCoder
、ISO8601DateCoder
) 进行自定义日期解码/编码。Base64Coder
以 base64 字符串表示形式解码/编码数据。还有更多,请参阅 HelperCoders
的完整文档以获取更多详细信息。
您甚至可以通过遵守 HelperCoder
来创建自己的。
例如,虽然 Swift
编译器仅生成假设外部标记枚举的实现,但只有以下数据
[
{
"load": {
"key": "MyKey"
}
},
{
"store": {
"key": "MyKey",
"value": 42
}
}
]
可以使用当前编译器实现的以下 enum
来表示
enum Command {
case load(key: String)
case store(key: String, value: Int)
}
而 MetaCodable
允许以上 enum
表示以下两种格式的数据
[
{
"type": "load",
"key": "MyKey"
},
{
"type": "store",
"key": "MyKey",
"value": 42
}
]
[
{
"type": "load",
"content": {
"key": "MyKey"
}
},
{
"type": "store",
"content": {
"key": "MyKey",
"value": 42
}
}
]
有关 API 详细信息和高级用例,请参阅 MetaCodable
和 HelperCoders
的完整文档。 此外,查看限制。
如果您希望贡献更改、提出任何改进建议,请查看我们的贡献指南,检查是否有未解决的问题(是否已在处理中),或者打开一个拉取请求。
MetaCodable
在 MIT 许可下发布。 有关详细信息,请参见 LICENSE。