Last active
February 25, 2022 13:04
-
-
Save anthann/a638ca1cd7f82f5bdfa48a6560cf7900 to your computer and use it in GitHub Desktop.
[Swift] Codable
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 0x01 **/ | |
struct Animal: Codable { | |
let name: String | |
let age: Int | |
} | |
let animal = Animal(name: "旺财", age: 1) | |
let jsonEncoder = JSONEncoder() | |
let jsonData = try jsonEncoder.encode(animal) | |
if let str = String(data: jsonData, encoding: .utf8) { | |
print(str) | |
// 输出{"name":"旺财","age":1} | |
} | |
// 用JSONDecoder把JSON Data转回instance | |
let jsonDecoder = JSONDecoder() | |
let decodedAnimal = try jsonDecoder.decode(Animal.self, from: jsonData) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 0x03 **/ | |
class Creature: Codable { | |
var name: String | |
var age: Int | |
init(name: String, age: Int) { | |
self.name = name | |
self.age = age | |
} | |
} | |
class Animal: Creature { | |
var hasLeg: Bool | |
init(name: String, age: Int, hasLeg: Bool) { | |
self.hasLeg = hasLeg | |
super.init(name: name, age: age) | |
} | |
required init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
hasLeg = try container.decode(Bool.self, forKey: .hasLeg) | |
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(hasLeg, forKey: .hasLeg) | |
let superdecoder = container.superEncoder() | |
try super.encode(to: superdecoder) | |
} | |
enum CodingKeys: String, CodingKey { | |
case hasLeg | |
} | |
} | |
class Plant: Creature { | |
var height: Double | |
init(name: String, age: Int, height: Double) { | |
self.height = height | |
super.init(name: name, age: age) | |
} | |
required init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
height = try container.decode(Double.self, forKey: .height) | |
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(height, forKey: .height) | |
let superdecoder = container.superEncoder() | |
try super.encode(to: superdecoder) | |
} | |
enum CodingKeys: String, CodingKey { | |
case height | |
} | |
} | |
/* Creature */ | |
let creature = Creature(name: "some_name", age: 12) | |
let creatureJsonData = try JSONEncoder().encode(creature) | |
if let str = String(data: creatureJsonData, encoding: .utf8) { | |
print(str) | |
// {"name":"some_name","age":12} | |
} | |
// 用JSONDecoder把JSON Data转回instance | |
let decodedCreature: Creature = try JSONDecoder().decode(Creature.self, from: creatureJsonData) | |
/* Animal */ | |
let animal = Animal(name: "miaomiao", age: 2, hasLeg: true) | |
let animalJsonData = try JSONEncoder().encode(animal) | |
if let str = String(data: animalJsonData, encoding: .utf8) { | |
print(str) | |
// {"super":{"name":"miaomiao","age":2},"hasLeg":true} | |
} | |
// 用JSONDecoder把JSON Data转回instance | |
let decodedAnimal: Animal = try JSONDecoder().decode(Animal.self, from: animalJsonData) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** 0x02 **/ | |
struct Person { | |
enum Gender: Int, Codable { | |
case male = 0, female | |
} | |
let name: String | |
let gender: Gender? | |
let birthday: String // Format of yyyy-MM-dd | |
init(name: String, gender: Gender?, birthday: String) { | |
self.name = name | |
self.gender = gender | |
self.birthday = birthday | |
} | |
static func birthdayFormatter() -> DateFormatter { | |
let formatter = DateFormatter() | |
formatter.dateFormat = "yyyy-MM-dd" | |
return formatter | |
} | |
} | |
extension Person: Codable { | |
enum CodingKeys: String, CodingKey { | |
case name | |
case gender | |
case birthday = "timestamp_birthday" | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.container(keyedBy: CodingKeys.self) | |
try container.encode(name, forKey: .name) | |
try container.encodeIfPresent(gender, forKey: .gender) | |
guard let timestamp: TimeInterval = Person.birthdayFormatter().date(from: birthday)?.timeIntervalSince1970 else { | |
fatalError("invalid birthday") | |
} | |
try container.encode(timestamp, forKey: .birthday) | |
} | |
init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
name = try container.decode(String.self, forKey: .name) | |
gender = try container.decodeIfPresent(Gender.self, forKey: .gender) | |
let timestamp: TimeInterval = try container.decode(TimeInterval.self, forKey: .birthday) | |
birthday = Person.birthdayFormatter().string(from: Date(timeIntervalSince1970: timestamp)) | |
} | |
} | |
let ming = Person(name: "小明", gender: .male, birthday: "1999-02-11") | |
let jsonData = try JSONEncoder().encode(ming) | |
if let str = String(data: jsonData, encoding: .utf8) { | |
print(str) | |
// {"name":"小明","timestamp_birthday":918662400,"gender":0} | |
} | |
// 用JSONDecoder把JSON Data转回instance | |
let decodedMing = try JSONDecoder().decode(Person.self, from: jsonData) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 上接 polymorphism_object.swift | |
struct MetaArray<M: Meta>: Codable, ExpressibleByArrayLiteral { | |
let array: [M.Element] | |
init(_ array: [M.Element]) { | |
self.array = array | |
} | |
init(arrayLiteral elements: M.Element...) { | |
self.array = elements | |
} | |
enum CodingKeys: String, CodingKey { | |
case metatype | |
case object | |
} | |
init(from decoder: Decoder) throws { | |
var container = try decoder.unkeyedContainer() | |
var elements: [M.Element] = [] | |
while !container.isAtEnd { | |
let nested = try container.nestedContainer(keyedBy: CodingKeys.self) | |
let typeStr = try nested.decode(String.self, forKey: .metatype) | |
let metatype = M.metatype(for: typeStr) | |
let superDecoder = try nested.superDecoder(forKey: .object) | |
let object = try metatype.type.init(from: superDecoder) | |
if let element = object as? M.Element { | |
elements.append(element) | |
} | |
} | |
array = elements | |
} | |
func encode(to encoder: Encoder) throws { | |
var container = encoder.unkeyedContainer() | |
try array.forEach { object in | |
let typeStr = String(describing: type(of: object)) | |
var nested = container.nestedContainer(keyedBy: CodingKeys.self) | |
try nested.encode(typeStr, forKey: .metatype) | |
let superEncoder = nested.superEncoder(forKey: .object) | |
let encodable = object as? Encodable | |
try encodable?.encode(to: superEncoder) | |
} | |
} | |
} | |
let creatures: [Creature] = [ | |
Animal(name: "miaomiao", age: 2, hasLeg: true), | |
Plant(name: "tree", age: 463, height: 32.1), | |
Creature(name: "WangWang", age: 2) | |
] | |
let creaturesJsonData = try JSONEncoder().encode(MetaArray<CreatureMetaType>(creatures)) | |
if let str = String(data: creaturesJsonData, encoding: .utf8) { | |
print(str) | |
// [{"metatype":"Animal","object":{"super":{"name":"miaomiao","age":2},"hasLeg":true}},{"metatype":"Plant","object":{"super":{"name":"tree","age":463},"height":32.100000000000001}},{"metatype":"Creature","object":{"name":"WangWang","age":2}}] | |
} | |
let decodedMetaArray: MetaArray<CreatureMetaType> = try JSONDecoder().decode(MetaArray<CreatureMetaType>.self, from: creaturesJsonData) | |
let decodedCreatures = decodedMetaArray.array |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Swift无法使用type string来构造Type,因此对每个使用了多态的类簇,实现一个遵守此协议的enum,间接获取Type。 | |
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)) | |
// print(classStr) | |
// let metatype = M.metatype(for: object) | |
try container.encode(typeStr, forKey: .metatype) | |
let superEncoder = container.superEncoder(forKey: .object) | |
let encodable = object as? Encodable | |
try encodable?.encode(to: superEncoder) | |
} | |
} | |
enum CreatureMetaType: String, Meta { | |
typealias Element = Creature | |
case creature = "Creature" | |
case animal = "Animal" | |
case plant = "Plant" | |
static func metatype(for typeString: String) -> CreatureMetaType { | |
guard let metatype = self.init(rawValue: typeString) else { | |
fatalError() | |
} | |
return metatype | |
} | |
var type: Decodable.Type { | |
switch self { | |
case .creature: | |
return Creature.self | |
case .animal: | |
return Animal.self | |
case .plant: | |
return Plant.self | |
} | |
} | |
} | |
class Creature: Codable { | |
var name: String | |
var age: Int | |
init(name: String, age: Int) { | |
self.name = name | |
self.age = age | |
} | |
} | |
class Animal: Creature { | |
var hasLeg: Bool | |
init(name: String, age: Int, hasLeg: Bool) { | |
self.hasLeg = hasLeg | |
super.init(name: name, age: age) | |
} | |
required init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
hasLeg = try container.decode(Bool.self, forKey: .hasLeg) | |
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(hasLeg, forKey: .hasLeg) | |
let superdecoder = container.superEncoder() | |
try super.encode(to: superdecoder) | |
} | |
enum CodingKeys: String, CodingKey { | |
case hasLeg | |
} | |
} | |
class Plant: Creature { | |
var height: Double | |
init(name: String, age: Int, height: Double) { | |
self.height = height | |
super.init(name: name, age: age) | |
} | |
required init(from decoder: Decoder) throws { | |
let container = try decoder.container(keyedBy: CodingKeys.self) | |
height = try container.decode(Double.self, forKey: .height) | |
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(height, forKey: .height) | |
let superdecoder = container.superEncoder() | |
try super.encode(to: superdecoder) | |
} | |
enum CodingKeys: String, CodingKey { | |
case height | |
} | |
} | |
/* Animal */ | |
let creature: Creature = Animal(name: "miaomiao", age: 2, hasLeg: true) | |
let creatureJsonData = try JSONEncoder().encode(MetaObject<CreatureMetaType>(creature)) | |
if let str = String(data: creatureJsonData, encoding: .utf8) { | |
print(str) | |
// {"metatype":"Animal","object":{"super":{"name":"miaomiao","age":2},"hasLeg":true}} | |
} | |
// 用JSONDecoder把JSON Data转回instance | |
let decodedMetaObject: MetaObject<CreatureMetaType> = try JSONDecoder().decode(MetaObject<CreatureMetaType>.self, from: creatureJsonData) | |
let decodedCreature = decodedMetaObject.object |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment