Swift继承Codable类JSON编码异常及移除super键咨询
Codable继承类编码问题:子类属性丢失&移除多余的
super键 问题背景
你定义了继承自Member(基类,遵循Codable)的MemberSingUp子类,实例化赋值后编码JSON时发现子类的Password等属性没有被包含;后来自定义了编解码方法,但又出现了嵌套的super键,想去掉这个键。
一、为什么子类属性没被编码?
Swift的Codable自动合成机制有个核心限制:当子类继承自一个遵循Codable的基类时,系统不会自动为子类合成包含新增属性的编解码逻辑,除非满足两个前提:
- 基类的
init(from: Decoder)方法被标记为required(确保子类能正确调用父类的解码逻辑) - 子类显式定义自己的
CodingKeys,明确列出需要编解码的子类属性
你的场景里,基类Member继承了NSObject,系统自动合成的Codable方法没有标记required,导致子类无法继承并扩展编解码逻辑,最终编码时只处理了父类的属性,子类的Password等被直接忽略。
二、如何移除自定义编解码中的super键?
你之前用superEncoder()和superDecoder()会把父类的编码结果放到嵌套的super键里,这是因为这两个方法会创建独立的嵌套编码器/解码器。要让父类和子类的属性平级出现在JSON中,需要让父类和子类共享同一个编码容器,而非嵌套容器。
正确的自定义编解码实现步骤:
- 给基类显式实现
Codable方法并标记required,确保子类能继承调用 - 子类定义自己的
CodingKeys,包含所有新增属性 - 在编码/解码时,先调用父类的编解码逻辑操作当前容器,再处理子类属性
修改后的完整代码:
基类Member(确保Codable方法可被子类继承)
class Member: NSObject, Codable { var Address: String? var BriefDescription: String? var City: String? // 显式声明required的Codable初始化方法,供子类调用 required init(from decoder: Decoder) throws { try super.init() let container = try decoder.container(keyedBy: CodingKeys.self) Address = try container.decodeIfPresent(String.self, forKey: .Address) BriefDescription = try container.decodeIfPresent(String.self, forKey: .BriefDescription) City = try container.decodeIfPresent(String.self, forKey: .City) } override init() { super.init() } func encode(to encoder: Encoder) throws { var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(Address, forKey: .Address) try container.encodeIfPresent(BriefDescription, forKey: .BriefDescription) try container.encodeIfPresent(City, forKey: .City) } enum CodingKeys: String, CodingKey { case Address, BriefDescription, City } }
子类MemberSingUp(共享同一编码容器)
class MemberSingUp: Member { var Password: String? var passwordAgain: String? var referansNumber: String? enum CodingKeys: String, CodingKey { case Password, passwordAgain, referansNumber } required init(from decoder: Decoder) throws { // 先解码子类属性 let container = try decoder.container(keyedBy: CodingKeys.self) Password = try container.decodeIfPresent(String.self, forKey: .Password) passwordAgain = try container.decodeIfPresent(String.self, forKey: .passwordAgain) referansNumber = try container.decodeIfPresent(String.self, forKey: .referansNumber) // 调用父类解码逻辑,复用同一个decoder try super.init(from: decoder) } override func encode(to encoder: Encoder) throws { // 先调用父类编码逻辑,把父类属性写入当前容器 try super.encode(to: encoder) // 再将子类属性写入同一个容器 var container = encoder.container(keyedBy: CodingKeys.self) try container.encodeIfPresent(Password, forKey: .Password) try container.encodeIfPresent(passwordAgain, forKey: .passwordAgain) try container.encodeIfPresent(referansNumber, forKey: .referansNumber) } }
测试编码结果
运行你的编码代码后,输出的JSON会是平级结构,没有super键:
{"Address":"Adress","BriefDescription":"BriefDescription","City":"City","Password":"123456"}
关键知识点总结
- 继承
NSObject的Codable类需要显式实现编解码方法并标记required,否则子类无法正确扩展 - 避免使用
superEncoder()/superDecoder(),直接复用同一个编码器/解码器容器,就能得到平级的JSON结构 - 使用
decodeIfPresent和encodeIfPresent处理可选属性,避免属性为nil时出现编码错误
内容的提问来源于stack exchange,提问作者Omer Tekbiyik
相关产品推荐
相关产品推荐

