这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战json
struct LGTeacher: Codable{
var name: String
var className: String
var courceCycle: Int
var personInfo: PersonInfo
}
extension LGTeacher {
struct PersonInfo: Codable {
var age: Int
var height: Double
}
}
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10, "personInfo": { "age": 18, "height": 1.85 } } """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
if let data = jsonData{
let result = try? decoder.decode(LGTeacher.self, from: data)
print(result ?? "解析失败")
}
复制代码
struct LGTeacher: Codable{
var name: String
var className: String
var courceCycle: Int
var personInfo: [PersonInfo]
}
extension LGTeacher {
struct PersonInfo: Codable {
var age: Int
var height: Double
}
}
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10, "personInfo": [ { "age": 18, "height": 1.85 },{ "age": 20, "height": 1.75 } ] } """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
if let data = jsonData{
let result = try? decoder.decode(LGTeacher.self, from: data)
print(result ?? "解析失败")
}
复制代码
struct LGTeacher: Codable{
var name: String
var className: String
var courceCycle: Int
}
let jsonString = """ [ { "name": "Kody", "className": "Swift", "courceCycle": 12 },{ "name": "Cat", "className": "强化班", "courceCycle": 15 },{ "name": "Hank", "className": "逆向班", "courceCycle": 22 },{ "name": "Cooci", "className": "大师班", "courceCycle": 22 } ] """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
if let data = jsonData{
let result = try? decoder.decode([LGTeacher].self, from: data)
print(result ?? "解析失败")
}
复制代码
**swift
let jsonString = """ [ { "name": "Kody", "className": "Swift", "courceCycle": 12 },{ "name": "Cat", "className": "强化班", "courceCycle": 15 },{ "name": "Hank", "className": null, "courceCycle": 22 },{ "name": "Cooci", "className": "大师班", "courceCycle": 22 } ] """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
if let data = jsonData{
let result = try? decoder.decode([LGTeacher].self, from: data)
print(result ?? "解析失败")
}
复制代码
好比咱们有一个坐标,location : [20, 10]
,当咱们在使用Codable
进行解析的过程当中,咱们须要进行以下操做:数组
struct Location: Codable {
var x: Double
var y: Double
init(from decoder: Decoder) throws{
var contaioner = try decoder.unkeyedContainer()
self.x = try contaioner.decode(Double.self)
self.y = try contaioner.decode(Double.self)
}
}
struct RawSeverResponse: Codable{
var location: Location
}
let jsonString = """ { "location": [20, 10] } """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
let result = try decoder.decode(RawSeverResponse.self, from: jsonData!)
print(result.location.x)
复制代码
class LGTeacher: Codable {
var name: String?
}
class LGPartTimeTeacher: LGTeacher {
var partTime: Int?
}
let jsonString = """ { "name": "Kody", "partTime": 20 } """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
let result = try decoder.decode(LGPartTimeTeacher.self, from: jsonData!)
print(result.name)
复制代码
protocol LGTeacher {
var name: String{ get set }
}
//
struct LGPartTimeTeacher: LGTeacher, Codable {
var name: String
var partTime: Int?
}
//
//
let jsonString = """ { "name": "Kody", "partTime": 20 } """
//
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
let result = try decoder.decode(LGPartTimeTeacher.self, from: jsonData!)
print(result)
复制代码
struct LGPerson: Decodable{
let elements: [String]
enum CodingKeys: String, CaseIterable, CodingKey {
case item0 = "item.0"
case item1 = "item.1"
case item2 = "item.2"
case item3 = "item.3"
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
var element: [String] = []
for item in CodingKeys.allCases{
guard container.contains(item) else { break }
element.append(try container.decode(String.self, forKey: item))
}
self.elements = element
}
}
//
//
let jsonString = """ { "item.3": "Kody", "item.0": "Hank", "item.2": "Cooci", "item.1": "Cat" } """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
let result = try decoder.decode(LGPerson.self, from: jsonData!)
print(result)
复制代码
咱们先来看一下 Codable
究竟是什么?
这里咱们使用一个简单的案例来看一下:markdown
struct LGTeacher: Codable {
var name: String
var className: String
var courceCycle: Int
}
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10 } """
let jsonData = jsonString.data(using: .utf8)
let decoder = JSONDecoder()
if let data = jsonData{
let result = try? decoder.decode(LGTeacher.self, from: data)
print(result ?? "解析失败")
}
复制代码
当前咱们建立一个解码的对象,而后调用 decode
方法将咱们的 json
字符串解析给咱们的模型 LGTeacher
。这里咱们须要探究的是它到底是如何工做的?
第一:咱们先来看一下 JSONDecoder
建立出来的对象app
public enum DateDecodingStrategy {
/// Defer to `Date` for decoding. This is the default strategy.
case deferredToDate
/// 表明距离 1970.01.01 的秒数
/// Decode the `Date` as a UNIX timestamp from a JSON number.
case secondsSince1970
/// 表明距离 1970.1.1 的毫秒数
/// Decode the `Date` as UNIX millisecond timestamp from a JSON number.
case millisecondsSince1970
/// Decode the `Date` as an ISO-8601-formatted string (in RFC 3339 format).
@available(OSX 10.12, iOS 10.0, watchOS 3.0, tvOS 10.0, *)
case iso8601
/// 后台自定义的格式,这个时候咱们能够本身建立 DateFormatter,来解析
/// Decode the `Date` as a string parsed by the given formatter.
case formatted(DateFormatter)
/// 自定义格式
/// Decode the `Date` as a custom value decoded by the given closure.
case custom((Decoder) throws -> Date)
}
复制代码
这里咱们来看一下实际的使用场景:ide
struct LGTeacher: Codable {
var name: String
var className: String
var courceCycle: Int
var date: Date
}
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10 "date": "1969-09-26T12:00:00Z" } """
复制代码
若是咱们直接使用默认的解析策略,那么这里代码运行以后,就会出现解析失败函数
decoder.dateDecodingStrategy = JSONDecoder.DateDecodingStrategy.iso8601
复制代码
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10, "date": 1609183207 } """
复制代码
decoder.dateDecodingStrategy = .secondsSince1970
复制代码
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10, "date": 1609183207000 } """
decoder.dateDecodingStrategy = .millisecondsSince1970
复制代码
let jsonString = """ { "name": "Kody", "className": "Swift", "courceCycle": 10, "date": "2020/12/28 19:20:00" } """
这种后台自定义的格式,须要咱们建立一个DateFormatter
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy/MM/dd HH:mm:ss"
decoder.dateDecodingStrategy = .formatted(dateFormatter)
复制代码
以上就是 JSONDecoder
的主要内容,定义了编码的策略,可让咱们根据不一样的场景来进行选择。
接下来咱们实际看一下当前是如何 decode
的oop
T
要求遵照 Decodable
协议。JSONSerializationg
对当前 data
进行序列话的操做_JSONDecoder
建立一个对象,而后调用 unBox
解码
这里咱们关注第三步和第四步,首先从第三步提及:这里返回了一个Decoder
的对象。
其中 storage
的实现:
回到咱们的第四步,unBox
就是开始拆盒子
能够看到这里就是匹配对应的类型,而后执行条件分支
回到咱们当前的main.swift
,这个时候咱们并无实现任何的方法
那么这里到底发生了什么?咱们借助咱们的老朋友SIL
来看一下:
也就意味着,当前编译器自动生成了一个默认的init(from:)
实现。咱们这里来一块儿阅读一下,发生了什么事情:
那查找的协议方法是什么?咱们回到刚才的_JSONDecoder
的实现:
咱们来比对一下:
这里就是再调用当前的找到的container
方法的实现;而这个方法在哪里实现了?是否是就是在_JSONDecoder
里面,咱们下一个断点看一下
接下来就有个问题了,是如何遍历咱们当前的 Key
值,来进行key-value
的赋值操做?首先咱们先看到 SIL
文件
首先在内存当中建立了枚举类型name
,而后调用decode()
方法,这个时候咱们在回到咱们的
找到KeyedDecodingContainer
的具体实现:
此时的Container
是什么?是否是就是
这里的Decode
方法是有_box
调用的,咱们回过头来在来找一下
基本上到这里咱们就明白了,实际上是在调用_JSONKeyedDecodingContainer
的decode
方法,这里咱们直接经过断点的方式来肯定一下:
post
上面的案例中,咱们在课程的刚开始讲过一个案例,那就是继承。那么若是咱们把案例修改一下:测试
class LGPerson: Codable {
var name: String?
var age: Int?
}
class LGTeacher: LGPerson {
var subjectName: String?
}
class LGPartTimeTeacher: LGPerson{
var partTime: Double?
}
let t = LGTeacher()
t.age = 10
t.name = "Kody"
t.subjectName = "Swift"
let encoder = JSONEncoder()
let encoderData = try encoder.encode(t)
print(String(data: encoderData, encoding: .utf8))
复制代码
咱们先来看一下上面这个案例可否正常编码成功
能够看到当前仅仅能正常编码成功咱们的 age
和 name
,可是咱们 subjectName
无法正常编码,咱们来 debug
看一下当前是为何?
咱们当前的 type
是 LGTeacher
并无遵照 if
的各类分支,因此当前代码就执行到一下分支:
那这里的 encode
方法在咱们遵照 Codable
协议以后,系统自动帮助咱们实现了,咱们经过 SIL
来看一下:
咱们再来看一下 LGPerson
默认的实现,因此天然而然咱们也就没办法访问到,当前的 LGPerson
的编码就会只对 age
, name
进行编码了。
class LGTeacher: LGPerson {
var subjectName: String?
// init(name: String, age: Int, subjectName: String) {
// self.subjectName = subjectName
// super.init(name: name, age: age)
// }
//
// required init(from decoder: Decoder) throws {
// fatalError("init(from:) has not been implemented")
// }
// enum CodingKeys: String, CodingKey{
// case subjectName
// }
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(subjectName, forKey: .subjectName)
let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)
}
}
复制代码
可是这样写的话,就会存在另外一个问题,由于当前的 CodingKeys
访问不到,因此这里咱们就须要这样操做
class LGTeacher: LGPerson {
var subjectName: String?
// init(name: String, age: Int, subjectName: String) {
// self.subjectName = subjectName
// super.init(name: name, age: age)
// }
//
// required init(from decoder: Decoder) throws {
// fatalError("init(from:) has not been implemented")
// }
enum CodingKeys: String, CodingKey{
case subjectName
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(subjectName, forKey: .subjectName)
let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)
}
}
复制代码
这样咱们就能正确编码了
若是咱们再把代码修改一下:
class LGPerson: Codable {
var name: String?
var age: Int?
init(name: String, age: Int) {
self.age = age
self.name = name
}
}
class LGTeacher: LGPerson {
var subjectName: String?
init(name: String, age: Int, subjectName: String) {
self.subjectName = subjectName
super.init(name: name, age: age)
}
required init(from decoder: Decoder) throws {
fatalError("init(from:) has not been implemented")
}
enum CodingKeys: String, CodingKey{
case subjectName
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(subjectName, forKey: .subjectName)
let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)
}
}
class LGPartTimeTeacher: LGPerson{
var partTime: Double?
}
let t: LGPerson = LGTeacher.init(name: "Kody", age: 10, subjectName: "Swift")
let encoder = JSONEncoder()
let encoderData = try encoder.encode(t)
print(String(data: encoderData, encoding: .utf8))
let t1: LGPerson = try JSONDecoder().decode(LGTeacher.self, from: encoderData)
复制代码
能够看到这里,就直接报错了:
看这里好像是由于咱们没有实现这个 decoder
的方法,那咱们来实现一下:
required init(from decoder: Decoder) throws {
// fatalError("init(from:) has not been implemented")
let container = try decoder.container(keyedBy: CodingKeys.self)
self.subjectName = try container.decode(String.self, forKey: .subjectName)
try super.init(from: decoder)
}
复制代码
let t1: LGPerson = try JSONDecoder().decode(LGTeacher.self, from: encoderData)
print(t1.age)
print(t1.name)
复制代码
并且若是当前属性是非可选项,那么这里咱们就直接会获得一个崩溃的应用程序。
若是这里咱们换成结构体,那么结果是否是同样哪?咱们先来测试一下:
protocol LGPerson: Codable {
var age: String { get set }
var name: String { get set }
}
struct LGTeacher: LGPerson {
var age: String
var name: String
}
struct LGParTimeTeacher: LGPerson {
var age: String
var name: String
}
struct Company: Codable{
var person: [LGPerson]
var companyName: String
enum CodingKeys: String, CodingKey {
case person
case companyName
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(person, forKey: .person)
try container.encode(companyName, forKey: .companyName)
}
}
复制代码
当前编译器就直接报错了,由于当前 LGPerson
是个协议,他不能遵照他自身
那么怎么办那?这个时候咱们可能想到的是直接在 LGTeacher
, LGPartTimeTeacher
中实现 decode 和 encode
的方法,那咱们能够找一个中间层来解决这个问题:
protocol LGPerson{
var age: String { get set }
var name: String { get set }
}
struct LGTeacher: LGPerson {
var age: String
var name: String
}
struct LGParTimeTeacher: LGPerson {
var age: String
var name: String
}
struct LGPersonBox : LGPerson, Codable {
var age: String
var name: String
init(_ person: LGPerson) {
self.age = person.age
self.name = person.name
}
}
struct Company: Codable{
var person: [LGPersonBox]
var companyName: String
}
复制代码
protocol LGPerson{
var age: Int { get set }
var name: String { get set }
}
struct LGTeacher: LGPerson {
var age: Int
var name: String
}
struct LGParTimeTeacher: LGPerson {
var age: Int
var name: String
}
struct LGPersonBox : LGPerson, Codable {
var age: Int
var name: String
init(_ person: LGPerson) {
self.age = person.age
self.name = person.name
}
}
struct Company: Codable{
var person: [LGPersonBox]
var companyName: String
}
let person: [LGPerson] = [LGTeacher(age: 20, name: "Kody"),LGParTimeTeacher(age: 30, name: "Hank")]
let company = Company(person: person.map(LGPersonBox.init), companyName: "Logic")
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try jsonEncoder.encode(company)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
复制代码
能够看到这里输出的格式都是 LGPersonBox
,若是咱们想正确的还原咱们当前的类型信息,应该作什么哪?很简单的一种作法就是须要在编码过程当中将咱们的类型信息编码进去,什么意思那?咱们经过代码来看一下:
enum LGPersonType:String, Codable {
case teacher
case partTeacher
var metdadata: LGPerson.Type {
switch self {
case .teacher:
return LGTeacher.self
case .partTeacher:
return LGParTimeTeacher.self
}
}
}
protocol LGPerson: Codable{
static var type: LGPersonType{ get }
var age: Int { get set }
var name: String { get set }
}
struct LGTeacher: LGPerson {
static var type: LGPersonType = LGPersonType.teacher
var age: Int
var name: String
}
struct LGParTimeTeacher: LGPerson {
static var type: LGPersonType = LGPersonType.partTeacher
var age: Int
var name: String
}
struct LGPersonBox : Codable {
var p: LGPerson
init(_ p: LGPerson) {
self.p = p
}
private enum CodingKeys : CodingKey {
case type
case p
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let type = try container.decode(LGPersonType.self, forKey: .type)
self.p = try type.metdadata.init(from: container.superDecoder(forKey: .p))
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type(of: p).type, forKey: .type)
try p.encode(to: container.superEncoder(forKey: .p))
}
}
struct Company: Codable{
var person: [LGPersonBox]
var companyName: String
}
let person: [LGPerson] = [LGTeacher(age: 20, name: "Kody"),LGParTimeTeacher(age: 30, name: "Hank")]
let company = Company(person: person.map(LGPersonBox.init), companyName: "Logic")
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try jsonEncoder.encode(company)
if let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
}
复制代码
固然若是咱们想输出下面这个
新的实现方式
protocol Meta: Codable {
associatedtype Element
static func metatype(for typeString: String) -> Self
var type: Decodable.Type { get }
}
struct MetaObject<M: Meta>: Codable {
let object: M.Element
init(_ object: M.Element) {
self.object = object
}
enum CodingKeys: String, CodingKey {
case metatype
case object
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let typeStr = try container.decode(String.self, forKey: .metatype)
let metatype = M.metatype(for: typeStr)
let superDecoder = try container.superDecoder(forKey: .object)
let obj = try metatype.type.init(from: superDecoder)
guard let element = obj as? M.Element else {
fatalError()
}
self.object = element
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
let typeStr = String(describing: type(of: object))
try container.encode(typeStr, forKey: .metatype)
let superEncoder = container.superEncoder(forKey: .object)
let encodable = object as? Encodable
try encodable?.encode(to: superEncoder)
}
}
enum LGPersonType: String, Meta {
typealias Element = LGPerson
case teacher = "LGTeacher"
case partTimeTeacher = "LGPartTimeTeacher"
static func metatype(for typeString: String) -> LGPersonType {
guard let metatype = self.init(rawValue: typeString) else {
fatalError()
}
return metatype
}
var type: Decodable.Type {
switch self {
case .teacher:
return LGTeacher.self
case .partTimeTeacher:
return LGPartTimeTeacher.self
}
}
}
class LGPerson: Codable {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
class LGTeacher: LGPerson {
var subjectName: String
init(name: String, age: Int, subjectName: String) {
self.subjectName = subjectName
super.init(name: name, age: age)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
subjectName = try container.decode(String.self, forKey: .subjectName)
let superDecoder = try container.superDecoder()
try super.init(from: superDecoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(subjectName, forKey: .subjectName)
let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)
}
enum CodingKeys: String, CodingKey {
case subjectName
}
}
class LGPartTimeTeacher: LGPerson {
var partTime: Double
init(name: String, age: Int, partTime: Double) {
self.partTime = partTime
super.init(name: name, age: age)
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
partTime = try container.decode(Double.self, forKey: .partTime)
let superDecoder = try container.superDecoder()
try super.init(from: superDecoder)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(partTime, forKey: .partTime)
let superdecoder = container.superEncoder()
try super.encode(to: superdecoder)
}
enum CodingKeys: String, CodingKey {
case partTime
}
}
let p: LGPerson = LGTeacher(name: "Kody", age: 20, subjectName: "Swift")
let jsonData = try JSONEncoder().encode(MetaObject<LGPersonType>(p))
if let str = String(data: jsonData, encoding: .utf8) {
print(str)
}
let decode: MetaObject<LGPersonType> = try JSONDecoder().decode(MetaObject<LGPersonType>.self, from: jsonData)
复制代码