Codable
プロトコルを使用して、Web APIからJSONをデコードしています。私のSwift
このAPIのデータモデルには、クラスの継承(サブクラス)とコンポジション(他のオブジェクトのプロパティとしてのオブジェクト)の両方が含まれています。 JSONでは、同じプロパティ名が完全なオブジェクト、またはデータベース内のそのオブジェクトのIDを示す単一の文字列を表す場合があります。decoder.container(keyedBy :)はDecodingError.typeMismatchエラーをスローします。コーディング可能なバグ?
Codable
を使用してこの種のJSONを処理する唯一のパターンは、オブジェクトの初期化子init(from decoder: Decoder)
内で "手動で"デコードを行い、最初にオブジェクト全体のデコードを試みることです。それが失敗した場合(キャッチされなければならないエラーをスローすることによって)、String
と同じプロパティのデコードを再試行します。
varientプロパティを含むオブジェクトが別のDecodable
クラスのサブクラスでない限り、これはうまくいきます。その場合、基本クラスのプロパティをデコードすると、関数decoder.container(keyedBy:)
の呼び出しにエラーDecodingError.typeMismatch
がスローされます。
下記のサンプルコードをご覧ください。
これは既知のバグですか?そして/または私はこの状況で別のデコード方法を見逃していますか?
DecodingError.typeMismatch
エラーがスローされた後にdecoder.container(keyedBy:)
が呼び出された場合、そのエラーがキャッチされても、同じエラーが単一の関数内にスローされます。
import Foundation
// A `Base` class
class Base: Codable {
var baseProperty: String? = "baseProperty"
init() {}
private enum CodingKeys: String, CodingKey { case baseProperty }
required init(from decoder: Decoder) throws {
//===>> The next line will throw DecodingError.typeMismatch
let container = try decoder.container(keyedBy: CodingKeys.self)
baseProperty = try container.decode(String.self, forKey: .baseProperty)
}
}
// A Subclass of `Base`
class Sub: Base {
// An `Other` class which is a property of the `Sub` class
class Other: Codable { var id: String? = "otherID" }
var subProperty: Other? = nil
override init() { super.init() }
private enum CodingKeys: String, CodingKey { case subProperty }
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
do { subProperty = try container.decode(Other.self,
forKey: .subProperty)
}
catch { // We didn't find a whole `Other` object in the JSON; look for a `String` instead
let s = try container.decode(String.self, forKey: .subProperty)
subProperty = Other()
subProperty?.id = s
}
try super.init(from: decoder)
}
}
// Some sample JSON data:
let json = """
{"baseProperty" : "baseProperty",
"subProperty" : "someIDString"}
""".data(using: .utf8)!
// MAIN program -----------------------------------------------------
// Decode the JSON to produce a new Sub class instance
do {
_ = try JSONDecoder().decode(Sub.self, from: json)
}
catch DecodingError.typeMismatch(_, let context) {
print("DecodingError.typeMismatch: \(context.debugDescription)")
print("DecodingError.Context: codingPath:")
for i in 0..<context.codingPath.count { print(" [\(i)] = \(context.codingPath[i])") }
}
OMG私はあなたにビールを借りています。 ;-) –