Mappable
Mappable 是一个轻量级、灵活、易于使用的框架,用于将 JSON 转换为模型,特别优化了不变属性初始化。
struct Flight: Mappable {
let number: String
let time: Date
init(map: Mapper) throws {
// with the help of @dynamicMemberLookup feature
number = try map.id()
time = try map.time()
// or use the old way
// number = try map.from("id")
// time = try map.from("time")
}
}
// Flight(JSONString: json)
还提供 xcode 插件 以生成自动实现。
特性
- 仅通过指定映射关系来将 JSON 转换为对象
- 针对不可变和可选优化
- 灵活:便于与手动初始化混合
- 兼容类型转换:例如,整数属性可以使用字符串值初始化
- 支持键路径
为什么还需要另一个?
大多数 JSON 到模型库无法很好地处理不可变属性初始化。它们需要使用 var
声明属性并具有可空类型,这破坏了 Swift 的简洁,并导致编写出糟糕的代码。Mappable 就是为了解决这个问题而生。
优点 | 缺点 | |
---|---|---|
Codable | - Swift 中的原生 - 自动(无需映射关系) - 支持双向转换 |
- 不可灵活 - 不支持继承类 |
HandyJSON | - 自动(无需映射关系) - 支持双向转换 |
不支持不可变属性 |
ObjectMapper | 双向转换 | - 不可变属性支持较弱* - 多种模式会导致混乱 - 支持某些类型组合的缺失。 |
SwiftyJSON | 不是 JSON 对象转换器。 它仅是一个处理 JSON 数据的便捷工具。 |
* 1) 不能方便地处理可选。2) 不支持兼容类型转换,这会导致整个对象在 JSON 中出现小的格式错误。
Mappable 高度借鉴了 ObjectMapper。您可以将 Mappable 视为 ObjectMapper 中 ImmutableMappable
的改进版本。
使用方法
基础知识
要支持映射,类型应该实现 Mappable
协议,这个协议只包含一个初始化方法
class Country: Mappable {
let name: String
let cities: [City] // struct City: Mappable { ... }
let atContinent: Continent // enum Continent: Mappable { ... }
required init(map: Mapper) throws {
name = try map.from("name")
cities = try map.from("city")
atContinent = try map.from("location.continent")
}
}
您只需编写映射关系:一个属性的键路径。尽管这些行只是普通的赋值语句,但不需要指定类型,所以可以将这些行视为映射关系的特殊表示。 (您可以将该行读作“尝试 (从) 将 (值) 映射到 XXX”)
然后您可以初始化对象如下
// NOTE: these initializer throw errors, you should do error handling
let c = try Country(JSON: jsonDict)
let d = try? Country(JSONString: jsonString)
支持的类型
- 原始类型:
Int
、Double
、String
、Bool
、URL
、Date
... - 容器类型:
Array
、Dictionary
、Set
- 可选类型
- 枚举、结构体、对象
- 以上类型的任意组合
默认值
// just use `??`
cities = try map.from("city") ?? []
可选处理
Optional
类型的值即使 JSON 中没有对应的数据或日期格式错误,也不会抛出错误。在这种情况下,将分配一个 nil
值。
如果您将属性声明为可选的,这可能会意味着该数据在 JSON 中不是严格必需的。因此,如果实际上没有数据,您希望获取一个 nil 值。
struct User: Mappable {
let ID: String
let summary: String?
init(map: Mapper) throws {
ID = try map.from("id")
summary = try map.from("summary")
}
}
let json = ["id": "a123"]
let user = try! User(JSONObject: json) // It won't crash.
兼容类型转换
从下面转换 | |
---|---|
整型,双精度浮点数,单精度浮点数,CGFloat | 字符串 |
布尔值 | 整型,"true","True","TRUE","YES","false","False","FALSE","NO","0","1" |
字符串 | 整型,NSNumber |
URL | 字符串 |
日期 | 从1970年1月1日开始的秒数,格式为字符串(例如:2016-06-13T16:00:00+00:00 ) |
更多详情请参考此处。
自定义转换
初始化器中的内容只是普通的赋值,因此你可以对数据进行任何操作。使用map.getRootValue()
和map.getValue(keyPath:)
来获取原始JSON值并执行所需的操作。
为了方便进行日期转换,在Mapper
中也有options
属性来设置自定义日期策略。(更复杂的示例 在此)
枚举
符合RawRepresentable
的枚举有一个默认的Mappable
实现。您只需在枚举类型上声明符合Mappable
,然后它就会工作。
对于有关联值的枚举,您可以手动实现。
enum EnumWithValues: Mappable {
case a(Int)
case b(String)
init(map: Mapper) throws {
// It could be initialized with a json date like:
// {"type": "a", value: 123}
let value = try map.getValue("type", as: String.self)
switch value {
case "a":
self = .a(try map.from("value"))
case "b":
self = .b(try map.from("value"))
default:
throw ErrorType.JSONStructureNotMatchDesired(value, "string a/b")
}
}
}
类继承
class ChildModel: BaseModel {
let b : Int
required init(map: Mapper) throws {
b = try map.from("b")
try super.init(map: map)
}
}
嵌套键名路径
使用键名路径"AAA.BBB"在JSON中映射多级路径值
// let json = """ {"AAA": {"BBB": 1}} """
b = try map.from("AAA.BBB")
// use `n` to get a n-th value in array
// let json = """ {"AAA": [11,22,33]} """
b = try map.from("AAA.`2`") // b = 33
如果普通的键名自然包含.
,您可以使用如map.from("a.file", keyPathIsNested: false)
,将此键名作为一个单级路径处理。
安装
- 适用于Swift 5及4.2:v1.3+
- 适用于Swift 4.1及以下:v1.2.2
Cocoapods
pod 'Mappable'
Swift 包管理器
.Package(url: "https://github.com/leavez/Mappable.git", from: "1.5.0"),
Carthage
github "leavez/Mappable"
许可协议
Mappable 在 MIT 许可下可用。有关更多信息,请参阅 LICENSE 文件。