一个 Swift 库,用于将 Codable
类型序列化为 Any
和 UserDefaults
,以及从它们反序列化。
RawRepresentable
类型被编码为其原始值
// "fish"
let any = try KeyValueEncoder().encode(Food(rawValue: "fish"))
Collection
类型被编码为 [Any]
// ["fish", "chips"]
let any = try KeyValueEncoder().encode(["fish", "chips"])
结构体和类被编码为 [String: Any]
struct User: Codable {
var id: Int
var name: String
}
// ["id": 1, "name": "Herbert"]
let any = try KeyValueEncoder().encode(User(id: 1, name: "Herbert"))
从 Any
解码值
let food = try KeyValueDecoder().decode(Food.self, from: "fish")
let meals = try KeyValueDecoder().decode([String].self, from: ["fish", "chips"])
let user = try KeyValueDecoder().decode(User.self, from: ["id": 1, "name": "Herbert"])
当解码失败时,会抛出 DecodingError
。 Context
包含指向失败属性的 keyPath。
// throws DecodingError.typeMismatch 'Expected String at SELF[1], found Int'
let meals = try KeyValueDecoder().decode([String].self, from: ["fish", 1])
// throws DecodingError.valueNotFound 'Expected String at SELF[1].name, found nil'
let user = try KeyValueDecoder().decode(User.self, from: [["id": 1, "name": "Herbert"], ["id:" 2])
// throws DecodingError.typeMismatch 'Int at SELF[2], cannot be exactly represented by UInt8'
let ascii = try KeyValueDecoder().decode([UInt8].self, from: [10, 100, 1000])
可以通过设置策略来调整 Optional.none
的编码方式。
默认策略保留 Optional.none
let encoder = KeyValueEncoder()
encoder.nilEncodingStrategy = .default
// [1, 2, nil, 3]
let any = try encoder.encode([1, 2, Int?.none, 3])
使用占位符字符串保留与 PropertyListEncoder
的兼容性
encoder.nilEncodingStrategy = .stringNull
// [1, 2, "$null", 3]
let any = try encoder.encode([1, 2, Int?.none, 3])
使用 NSNull
保留与 JSONSerialization
的兼容性
encoder.nilEncodingStrategy = .nsNull
// [1, 2, NSNull(), 3]
let any = try encoder.encode([1, 2, Int?.none, 3])
Nil 值也可以被完全删除
encoder.nilEncodingStrategy = .removed
// [1, 2, 3]
let any = try encoder.encode([1, 2, Int?.none, 3])
可以通过 intDecodingStrategy
调整 BinaryInteger
类型(Int
、UInt
等)的解码。
默认策略 IntDecodingStrategy.exact
确保源值能够被解码类型精确表示,允许解码没有小数部分的浮点数值
// [10, 20, -30, 50]
let values = try KeyValueDecoder().decode([Int8].self, from: [10, 20.0, -30.0, Int64(50)])
// throws DecodingError.typeMismatch because 1000 cannot be exactly represented by Int8
_ = try KeyValueDecoder().decode(Int8.self, from: 1000])
带有小数部分的值也可以通过使用任何 FloatingPointRoundingRule
进行舍入来解码为整数
let decoder = KeyValueDecoder()
decoder.intDecodingStrategy = .rounding(rule: .toNearestOrAwayFromZero)
// [10, -21, 50]
let values = try decoder.decode([Int].self, from: [10.1, -20.9, 50.00001]),
值也可以被限制在可表示的范围内
let decoder = KeyValueDecoder()
decoder.intDecodingStrategy = .clamping(roundingRule: .toNearestOrAwayFromZero)
// [10, 21, 127, -128]
let values = try decoder.decode([Int8].self, from: [10, 20.5, 1000, -Double.infinity])
使用 UserDefaults
编码和解码 Codable
类型
try UserDefaults.standard.encode(
User(id: "1", name: "Herbert"),
forKey: "owner"
)
try UserDefaults.standard.encode(
URL(string: "fish.com"),
forKey: "url"
)
try UserDefaults.standard.encode(
Duration.nanoseconds(1),
forKey: "duration"
)
值以 plist 原生类型的友好的表示形式持久化存储
let defaults = UserDefaults.standard.dictionaryRepresentation()
[
"owner": ["id": 1, "name": "Herbert"],
"url": URL(string: "fish.com"),
"duration": [0, 1000000000]
]
从 defaults 中解码值
let owner = try UserDefaults.standard.decode(Person.self, forKey: "owner")
let url = try UserDefaults.standard.decode(URL.self, forKey: "url")
let duration = try UserDefaults.standard.decode(Duration.self, forKey: "duration")