Kev*_*own 59 swift swift4 codable
是否应该使用类继承来破坏类的可解码性.例如,以下代码
class Server : Codable {
var id : Int?
}
class Development : Server {
var name : String?
var userId : Int?
}
var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development
print(item.id ?? "id is nil")
print(item.name ?? "name is nil") here
Run Code Online (Sandbox Code Playgroud)
输出是:
1
name is nil
Run Code Online (Sandbox Code Playgroud)
现在,如果我反转这个,名称解码但id没有.
class Server {
var id : Int?
}
class Development : Server, Codable {
var name : String?
var userId : Int?
}
var json = "{\"id\" : 1,\"name\" : \"Large Building Development\"}"
let jsonDecoder = JSONDecoder()
let item = try jsonDecoder.decode(Development.self, from:json.data(using: .utf8)!) as Development
print(item.id ?? "id is nil")
print(item.name ?? "name is nil")
Run Code Online (Sandbox Code Playgroud)
输出是:
id is nil
Large Building Development
Run Code Online (Sandbox Code Playgroud)
并且你不能在两个班级中表达Codable.
Jos*_*zzi 65
我相信继承的情况你必须Coding
自己实现.也就是说,你必须指定CodingKeys
和执行init(from:)
,并encode(to:)
在这两个超和子类.根据WWDC视频(大约49:28,如下图所示),您必须使用超级编码器/解码器调用super.
required init(from decoder: Decoder) throws {
// Get our container for this subclass' coding keys
let container = try decoder.container(keyedBy: CodingKeys.self)
myVar = try container.decode(MyType.self, forKey: .myVar)
// otherVar = ...
// Get superDecoder for superclass and call super.init(from:) with it
let superDecoder = try container.superDecoder()
try super.init(from: superDecoder)
}
Run Code Online (Sandbox Code Playgroud)
视频似乎停止短显示编码端的(但它container.superEncoder()
的encode(to:)
一侧),但它在几乎相同的方式在encode(to:)
执行.我可以在这个简单的例子中确认这是有效的(参见下面的游乐场代码).
我仍在努力处理一些奇怪的行为,我正在使用一个更加复杂的模型来转换NSCoding
,它有许多新嵌套的类型(包括struct
和enum
),它们表现出这种意想不到的nil
行为并且"不应该".请注意,可能存在涉及嵌套类型的边缘情况.
编辑:嵌套类型似乎在我的测试操场中正常工作; 我现在怀疑自引用类(想想树节点的子节点)有一个错误,它有一个自身的集合,它也包含该类的各个子类的实例.对一个简单的自引用类的测试解码很好(也就是说,没有子类)所以我现在正在集中精力研究子类案例失败的原因.
更新于2017年6月25日:我最终向Apple提交了一个关于此问题的错误.rdar:// 32911973 - 不幸的是Superclass
,包含Subclass: Superclass
元素的数组的编码/解码周期将导致数组中的所有元素被解码为Superclass
(子类' init(from:)
永远不被调用,导致数据丢失或更糟).
//: Fully-Implemented Inheritance
class FullSuper: Codable {
var id: UUID?
init() {}
private enum CodingKeys: String, CodingKey { case id }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(UUID.self, forKey: .id)
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(id, forKey: .id)
}
}
class FullSub: FullSuper {
var string: String?
private enum CodingKeys: String, CodingKey { case string }
override init() { super.init() }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let superdecoder = try container.superDecoder()
try super.init(from: superdecoder)
string = try container.decode(String.self, forKey: .string)
}
override func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(string, forKey: .string)
let superencoder = container.superEncoder()
try super.encode(to: superencoder)
}
}
let fullSub = FullSub()
fullSub.id = UUID()
fullSub.string = "FullSub"
let fullEncoder = PropertyListEncoder()
let fullData = try fullEncoder.encode(fullSub)
let fullDecoder = PropertyListDecoder()
let fullSubDecoded: FullSub = try fullDecoder.decode(FullSub.self, from: fullData)
Run Code Online (Sandbox Code Playgroud)
超级和子类属性都将恢复fullSubDecoded
.
dev*_*jme 11
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(employeeID, forKey: .employeeID)
}
Run Code Online (Sandbox Code Playgroud)
对于解码,我这样做了:
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let values = try decoder.container(keyedBy: CodingKeys.self)
total = try values.decode(Int.self, forKey: .total)
}
private enum CodingKeys: String, CodingKey
{
case total
}
Run Code Online (Sandbox Code Playgroud)
Swift 在 5.1 中引入了 Property Wrappers 我实现了一个名为SerializedSwift的库,它使用属性包装器的强大功能将 JSON 数据解码和编码为对象。
我的主要目标之一是,使继承的对象开箱即用地解码,而无需额外的init(from decoder: Decoder)
覆盖。
import SerializedSwift
class User: Serializable {
@Serialized
var name: String
@Serialized("globalId")
var id: String?
@Serialized(alternateKey: "mobileNumber")
var phoneNumber: String?
@Serialized(default: 0)
var score: Int
required init() {}
}
// Inherited object
class PowerUser: User {
@Serialized
var powerName: String?
@Serialized(default: 0)
var credit: Int
}
Run Code Online (Sandbox Code Playgroud)
它还支持自定义编码键、备用键、默认值、自定义转换类以及未来将包含的更多功能。
小智 5
我能够通过使我的基类和子类符合Decodable
而不是Codable
. 如果我使用Codable
它,它会以奇怪的方式崩溃,例如EXC_BAD_ACCESS
在访问子类的字段时获取 a ,但调试器可以毫无问题地显示所有子类值。
此外,将 superDecoder 传递给基类super.init()
不起作用。我刚刚将解码器从子类传递到基类。
使用下面的方法怎么样?
protocol Parent: Codable {
var inheritedProp: Int? {get set}
}
struct Child: Parent {
var inheritedProp: Int?
var title: String?
enum CodingKeys: String, CodingKey {
case inheritedProp = "inherited_prop"
case title = "short_title"
}
}
Run Code Online (Sandbox Code Playgroud)
有关组合的其他信息:http://mikebuss.com/2016/01/10/interfaces-vs-inheritance/
这是一个库TypePreservingCodingAdapter来做到这一点(可以与 Cocoapods 或 SwiftPackageManager 一起安装)。
下面的代码编译并与 Swift 一起工作得很好4.2
。不幸的是,对于每个子类,您都需要自己实现属性的编码和解码。
import TypePreservingCodingAdapter
import Foundation
// redeclared your types with initializers
class Server: Codable {
var id: Int?
init(id: Int?) {
self.id = id
}
}
class Development: Server {
var name: String?
var userId: Int?
private enum CodingKeys: String, CodingKey {
case name
case userId
}
init(id: Int?, name: String?, userId: Int?) {
self.name = name
self.userId = userId
super.init(id: id)
}
required init(from decoder: Decoder) throws {
try super.init(from: decoder)
let container = try decoder.container(keyedBy: CodingKeys.self)
name = try container.decodeIfPresent(String.self, forKey: .name)
userId = try container.decodeIfPresent(Int.self, forKey: .userId)
}
override func encode(to encoder: Encoder) throws {
try super.encode(to: encoder)
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(userId, forKey: .userId)
}
}
// create and adapter
let adapter = TypePreservingCodingAdapter()
let encoder = JSONEncoder()
let decoder = JSONDecoder()
// inject it into encoder and decoder
encoder.userInfo[.typePreservingAdapter] = adapter
decoder.userInfo[.typePreservingAdapter] = adapter
// register your types with adapter
adapter.register(type: Server.self).register(type: Development.self)
let server = Server(id: 1)
let development = Development(id: 2, name: "dev", userId: 42)
let servers: [Server] = [server, development]
// wrap specific object with Wrap helper object
let data = try! encoder.encode(servers.map { Wrap(wrapped: $0) })
// decode object back and unwrap them force casting to a common ancestor type
let decodedServers = try! decoder.decode([Wrap].self, from: data).map { $0.wrapped as! Server }
// check that decoded object are of correct types
print(decodedServers.first is Server) // prints true
print(decodedServers.last is Development) // prints true
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
19701 次 |
最近记录: |