상속과 함께 Swift 4에서 Decodable 사용
클래스 상속을 사용하면 클래스의 Decodability가 깨집니다. 예를 들어, 다음 코드
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
출력은 다음과 같습니다.
1
name is nil
이제 이것을 뒤집 으면 이름은 해독되지만 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")
출력은 다음과 같습니다.
id is nil
Large Building Development
그리고 두 클래스 모두에서 Codable을 표현할 수 없습니다.
상속의 경우 Coding
스스로 구현해야한다고 믿습니다 . 즉, 지정해야한다 CodingKeys
하고 구현 init(from:)
및 encode(to:)
슈퍼 클래스와 서브 클래스 모두에서. 당 WWDC 비디오 (아래 그림 주위 49:28), 당신은 슈퍼 인코더 / 디코더와 슈퍼 호출해야합니다.
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)
}
비디오는 인코딩 측면을 보여주는 데 부족한 것처럼 보이지만 (측면을 container.superEncoder()
위한 encode(to:)
것임) encode(to:)
구현 에서 거의 동일한 방식으로 작동합니다 . 이 간단한 경우에 이것이 작동하는지 확인할 수 있습니다 (아래의 플레이 그라운드 코드 참조).
나는 이 예상치 못한 행동 NSCoding
을 보이고 "안돼야 만하는"많은 새로 중첩 된 유형 ( struct
및 포함 enum
)을 가지고있는 훨씬 더 복잡한 모델을 사용하여 여전히 이상한 행동으로 어려움을 겪고 nil
있습니다. 중첩 된 유형과 관련된 엣지 케이스가있을 수 있습니다.
편집 : 중첩 된 유형이 내 테스트 플레이 그라운드에서 잘 작동하는 것 같습니다. 이제 그 클래스의 다양한 하위 클래스의 인스턴스도 포함하는 자체 컬렉션이있는 자체 참조 클래스 (트리 노드의 자식을 생각)에 문제가 있다고 생각합니다. 간단한 자체 참조 클래스의 테스트는 잘 디코딩되므로 (즉, 하위 클래스 없음) 이제 하위 클래스 사례가 실패하는 이유에 대해 집중하고 있습니다.
업데이트 June 25 '17 : 결국이 문제에 대해 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)
수퍼 클래스 및 하위 클래스 속성은 모두 fullSubDecoded
.
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)
}
디코딩을 위해 다음을 수행했습니다.
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
}
내 기본 클래스와 하위 클래스를 Decodable
대신 Codable
. 내가 사용 하면 하위 클래스의 필드에 액세스 할 때 Codable
a를 얻는 것과 같은 이상한 방식으로 충돌이 발생 EXC_BAD_ACCESS
하지만 디버거는 문제없이 모든 하위 클래스 값을 표시 할 수 있습니다.
또한 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"
}
}
구성에 대한 추가 정보 : 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
참조 URL : https://stackoverflow.com/questions/44553934/using-decodable-in-swift-4-with-inheritance
'programing' 카테고리의 다른 글
어패류에서 변수 설정을 해제하려면 어떻게합니까? (0) | 2020.12.25 |
---|---|
일괄 처리를 위해 Spark / Flink에 비해 Apache Beam의 이점은 무엇인가요? (0) | 2020.12.25 |
각도 6 의존성 주입 (0) | 2020.12.25 |
컴파일 오류 : 인터페이스를 명시 적으로 구현하는 동안 "수정 자 'public'이이 항목에 유효하지 않습니다." (0) | 2020.12.25 |
@Column JPA 어노테이션에 설정된 경우 길이 속성은 무엇을합니까? (0) | 2020.12.25 |