Skip to content

Instantly share code, notes, and snippets.

@anthann
Last active February 25, 2022 13:04
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save anthann/a638ca1cd7f82f5bdfa48a6560cf7900 to your computer and use it in GitHub Desktop.
Save anthann/a638ca1cd7f82f5bdfa48a6560cf7900 to your computer and use it in GitHub Desktop.
[Swift] Codable
/** 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)
/** 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)
/** 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)
// 上接 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
// 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