一、Codable
typealias Codable = Decodable & Encodable
Codable是一个同时符合 Decodable 和 Encodable 协议的类型,即可解码且可编码的类型。Codable 是Swift 4 引入的全新编码库,使用JSONDecoder可以实现字典转模型,使用JSONEncoder可以实现模型转字典。
struct Good:Codable{
var name:String
var age:Int
}
func modelWithJson(){
let dic = ["name": "张三","age":20] as [String : Any]
let data = try! JSONSerialization.data(withJSONObject: dic)
let jsonDecoder = JSONDecoder()
let item = try! jsonDecoder.decode(Good.self, from: data)
}
func jsonWithModel(){
let item = Good(name: "张三", age: 20)
let jsonEnCoder = JSONEncoder()
let data = try! jsonEnCoder.encode(item)
if let dic = String.init(data: data, encoding: .utf8){
print(dic)
}
}
使用注意:属性age设置要成可选类型;如果json中没有包含age,非可选的话直接decoder模型会转换失败,而当age设置成可选类型时就可以成功成功转换
struct Good:Codable{
var name:String
var age:Int?
}
let dic = ["name": "张三"]
1.2、CodingKeys
在struct、class、enum中可以自定义一个CodingKeys( RawValue 为 String 类型,并符合 CodingKey 协议)
枚举可以 用来
- 1、当数据类型属性名和 JSON 中字段名不同时,做 key 的映射。
- 2、通过在不添加某些字段的 case,可以忽略某个key不进行编码
struct Good:Codable{
var name:String
var age:Int?
enum CodingKeys:String,CodingKey{
case age
case name = "nickName"
}
}
1.3、嵌套对象
只要这个嵌套对象也符合 Codable 协议,那整个对象就可以正常使用 JSONEncoder 和 JSONDecoder 编解码。
struct Goods: Codable {
var name: String
var icon: String
}
struct Person: Codable {
var good:Goods
var age:Int
}
let dic = ["age":10,
"good":["name":"jack","icon":"hhh"]
] as [String : Any]
let data = try! JSONSerialization.data(withJSONObject: dic)
let jsonDecoder = JSONDecoder()
let item = try! jsonDecoder.decode(Person.self, from: data)
print(item.good.name)
二、自定义Codable
class Student:Codable {
var name:String
var age:Int
var selected:Bool
enum CodingKeys:String,CodingKey{
case name
case age
case selected
}
//自定义解码
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.age = try container.decode(Int.self, forKey: .age)
self.selected = try container.decode(Bool.self, forKey: .selected)
}
//自定义编码
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try! container.encode(self.name, forKey: CodingKeys.name)
try! container.encode(self.age, forKey: CodingKeys.age)
try! container.encode(self.selected, forKey: CodingKeys.selected)
print(self.name)
print(self.age)
print(self.selected)
}
}
以上就是自定义codeble,其中container是一个解析容器,通过container来调用decode、encoder实现编解码.
Container在Decoder协议中知道,一共提供了三种:
- KeyedDecodingContainer 代表容器中保存的数据是按照键值对的形式保存的
- UnkeyedDecodingContainer 代表容器中保存的数据是没有键的,也就是说,保存的数据是一个数组
- SingleValueDecodingContainer 代表容器中只保存了一个值。
2.1、KeyedDecodingContainer
一般JSON数据能被序列化成字典的,用的就是KeyedDecodingContainer来decode的。
required init(from decoder: Decoder) throws { /// 通过从给定解码器解码来创建新实例。
let container: KeyedDecodingContainer = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.age = try container.decode(Int.self, forKey: .age)
self.selected = self.selected = try container.decode(Bool.self, forKey: .selected)
print(self.selected)
}
let json = """
{"name":"10","age":20,"selected":false}
"""
let data = json.data(using: .utf8)!
let decode = JSONDecoder();
let item = try! decode.decode(Student.self, from: data);
2.2、UnkeyedDecodingContainer
是没有键值的,保存的数据是一个数组。先举个例子说明,假如我们给我们的数据结构是这样的:["A",2,false],而我们想去分别对应a,b,c三个属性。我们就可以这样
class Student:Codable {
var a:String
var b:Int
var c:Bool
//自定义解码
required init(from decoder: Decoder) throws { /// 通过从给定解码器解码来创建新实例。
var container = try decoder.unkeyedContainer()
self.a = try container.decode(String.self)
self.b = try container.decode(Int.self)
self.c = try container.decode(Bool.self)
print(self.a,self.b,self.c)
}
}
let json = """
["A",2,false]
"""
let data = json.data(using: .utf8)!
let decode = JSONDecoder();
let item = try! decode.decode(Student.self, from: data);
UnkeyedDecodingContainer每次decode,下标会加1,所以会把数组里的值依次取出来赋值,所以多次decode,就可以给a,b,c依次赋值。
2.3、SingleValueDecodingContainer
SingleValueDecodingContainer中,container一般放的是String或者Int之类的,当然,也可以放数组字典,但是不在取里面的元素了,而是作为一个整体被解析。
class Student:Codable {
var name:String
//自定义解码
required init(from decoder: Decoder) throws { /// 通过从给定解码器解码来创建新实例。
let container = try decoder.singleValueContainer()
self.name = try container.decode(String.self)
print(self.name)
}
}
let json = """
"zhangsan"
"""
let data = json.data(using: .utf8)!
let decode = JSONDecoder();
let item = try! decode.decode(Student.self, from: data);
以上就是将一个String存储到singleValueContainer进行解析
//存放一个Int
struct TestInt:Codable{
var int:Int
init(from decoder: Decoder) throws {
self.int = try decoder.singleValueContainer().decode(Int.self)
print(self.int)
}
}
//存放一个Bool
struct TestBool:Codable{
var bool:Bool
init(from decoder: Decoder) throws {
self.bool = try decoder.singleValueContainer().decode(Bool.self)
print(self.bool)
}
}
//存放一个String
struct TestString:Codable{
var string:String
init(from decoder: Decoder) throws {
self.string = try decoder.singleValueContainer().decode(String.self)
print(self.string)
}
}
//存放一个字典
struct TestDictionary:Codable{
var dic:[String:String]
init(from decoder: Decoder) throws {
self.dic = try decoder.singleValueContainer().decode([String:String].self)
print(self.dic)
}
}
//存放一个数组
struct TestArray:Codable{
var array:[String]
init(from decoder: Decoder) throws {
self.array = try decoder.singleValueContainer().decode([String].self)
print(self.array)
}
}
func decoderTestInt(){
let data = """
1
"""
.data(using: .utf8)!
let decode = JSONDecoder();
let p = try! decode.decode(TestInt.self, from: data);
}
func decoderTestBool(){
let data = """
false
"""
.data(using: .utf8)!
let decode = JSONDecoder();
let p = try! decode.decode(TestBool.self, from: data);
}
func decoderTestString(){
let data = """
"1"
"""
.data(using: .utf8)!
let decode = JSONDecoder();
let p = try! decode.decode(TestString.self, from: data);
}
func decoderTestDic(){
let data = """
{
"id":"222"
}
"""
.data(using: .utf8)!
let decode = JSONDecoder();
let p = try! decode.decode(TestDictionary.self, from: data);
}
func decoderTestArray(){
let data = """
["aaa","bbb"]
"""
.data(using: .utf8)!
let decode = JSONDecoder();
let p = try! decode.decode(TestArray.self, from: data);
}
以上三者的区别总的说区别如下:
KeyedDecodingContainer,通过字典key来取值
let value = container[key]
UnkeyedDecodingContainer,通过数组下标来取值
let value = container[index]
SingleValueDecodingContainer,value就是container本身
let value = container
三、自定义Codable的使用。
尽管系统已帮我们默认实现了自定义编解码,但凡一个值decode或encoder失败就整个解析失败了。本地的数据我们严格编码自然是不会失败的,但是我们的数据一般是来自服务端的,服务端的数据类型和codable模型属性类型一个不匹配(例如 APP 端是 Int 类型,服务器下发的是 String 类型),或者是服务器下发的数据缺乏了某个字段,都会导致解析失败。这样事情我们是不希望发生的,所以就可以通过自定义Codable,在过程中进行容错处理,所以我觉得自定义Codable目前来说是十分必要的。
3.1、提供默认值
编译器自动生成的编解码实现有个问题就是不支持默认值。如果需要支持默认值就需要自己来用 decodeIfPresent 来实现:
class Student:Codable{
var name:String
var age:Int
var height:CGFloat
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decodeIfPresent(String.self, forKey: .name) ?? ""
self.age = try container.decodeIfPresent(Int.self, forKey: .age) ?? 0
self.height = try container.decodeIfPresent(CGFloat.self, forKey: .height) ?? 0.0
}
}
使用decodeIfPresent 提供一个默认值,这样即使服务端的数据缺乏了某个字段或者某个数据为 null时,都不会影响正常解码,极大减少了jsonEncoder的错误率
3.2、类型不一致强制处理
@propertyWrapper
struct DefaultSting:Codable{
var wrappedValue: String
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let str = try? container.decode(Int.self){
self.wrappedValue = String(str)
}else if let str = try? container.decode(String.self){
self.wrappedValue = str
} else{
self.wrappedValue = ""
}
}
init(){
self.wrappedValue = "aaa"
}
}
struct Student:Codable{
@DefaultSting var name:String
var age:String
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self._name = try container.decodeIfPresent(DefaultSting.self, forKey: .name) ?? DefaultSting()
self.age = try container.decodeIfPresent(String.self, forKey: .age) ?? ""
}
}
改进版:
import UIKit
protocol DefaultValue {
static var defaultValue: Self { get set}
}
extension String: DefaultValue {
static var defaultValue = ""
}
extension Int: DefaultValue {
static var defaultValue = 0
}
extension Float: DefaultValue {
static var defaultValue:Float = 0.0
}
extension Bool: DefaultValue {
static var defaultValue:Bool = false
}
extension Double: DefaultValue {
static var defaultValue:Double = 0.0
}
extension CGFloat: DefaultValue {
static var defaultValue:CGFloat = 0.0
}
typealias DefaultCodable = DefaultValue & Codable
@propertyWrapper
struct Default {
var wrappedValue: T
}
extension Default: Codable {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if T.defaultValue is String{
if let value = try? container.decode(Int.self){
T.defaultValue = String(value) as! T
}else
if let value = try? container.decode(Bool.self){
T.defaultValue = (value == true ? "0" : "1") as! T
}else if let value = try? container.decode(Double.self){
T.defaultValue = String(value) as! T
}else if let value = try? container.decode(Float.self){
T.defaultValue = String(value) as! T
}
}else if T.defaultValue is Int{
if let value = try? container.decode(Int.self){
T.defaultValue = Int(value) as! T
}else
if let value = try? container.decode(Bool.self){
T.defaultValue = (value == true ? 0 : 1) as! T
}else if let value = try? container.decode(Double.self){
T.defaultValue = Int(value) as! T
}else if let value = try? container.decode(Float.self){
T.defaultValue = Int(value) as! T
}else if let value = try? container.decode(String.self){
T.defaultValue = String(value) as! T
}
}else if T.defaultValue is Float{
if let value = try? container.decode(Int.self){
T.defaultValue = Float(value) as! T
}else if let value = try? container.decode(Double.self){
T.defaultValue = Float(value) as! T
}else if let value = try? container.decode(Float.self){
T.defaultValue = Float(value) as! T
}
else if let value = try? container.decode(String.self){
T.defaultValue = String(value) as! T
}
}else if T.defaultValue is CGFloat{
if let value = try? container.decode(Int.self){
T.defaultValue = CGFloat(value) as! T
}else if let value = try? container.decode(Double.self){
T.defaultValue = CGFloat(value) as! T
}else if let value = try? container.decode(Float.self){
T.defaultValue = CGFloat(value) as! T
}
else if let value = try? container.decode(String.self){
let double = Double(value)
T.defaultValue = CGFloat(double ?? 0.00) as! T
}
}
else if T.defaultValue is Bool{
if let value = try? container.decode(Int.self){
T.defaultValue = (value == 0 ? false : true) as! T
}
else if let value = try? container.decode(String.self){
T.defaultValue = (value == "0" ? false : true) as! T
}
}
wrappedValue = (try? container.decode(T.self)) ?? T.defaultValue
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(wrappedValue)
}
}
extension KeyedDecodingContainer {
func decode(_ type: Default.Type, forKey key: KeyedDecodingContainer.Key) throws -> Default where T : DefaultCodable {
try decodeIfPresent(type, forKey: key) ?? Default(wrappedValue: T.defaultValue)
}
}
extension UnkeyedDecodingContainer {
mutating func decode(_ type: Default.Type) throws -> Default where T : DefaultCodable {
try decodeIfPresent(type) ?? Default(wrappedValue: T.defaultValue)
}
}
struct Person:Codable{
@Default var p:String
}
struct Student:Codable{
@Default var name:String
var p:[Person]
}
参考学习:https://juejin.cn/post/6938388060367224869