Codable(Decodable)に関して、Modelを作ってるときは覚えているんだけど
忘れてしまうことが多いので、自分用の備忘録的に残しておく。
基本的には手動Codableの書き方。配列形式と、ネスト形式をよく忘れるので。
配列型の場合は.nestedUnkeyedContainer
、
辞書型の場合は.nestedContainer
を使います。
参考
Codable
Codableを親クラスとして指定すると、Encodable + Decodableのメソッドがデフォルトで設定されている。
CodingKeysなどについての詳しいところは、参考記事の方で解説されているので省略
struct User: Codable {
let name: String
let age: Int
// ここから以下がデフォルトで定義されている(変更の必要がなければ、書かなくて良い)
enum CodingKeys: String, CodingKey {
case name
case age
}
}
// 本来ならここから下を書く場合には、UserからCodableのプロトコルは外す
extension User:Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(age, forKey: .age)
}
}
extension User:Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
age = try values.decode(Int.self, forKey: .age)
}
}
手動Codable
自動に任せれば楽なんだけど、そうするとModelの形がAPIに引きづられてしまうので使いにくいことも多い。
そこで手動でEncode / Decodeを書く必要が出てくるんだけどこれがちょっと厄介というか・・・。
なんか普通にJSONパーサーに任せた方がいいんじゃなかろうかとか思い始める。
配列を手動で解析する場合
struct User {
let name: String
let age: Int
let friends: [Friend]
enum CodingKeys: String, CodingKey {
case name
case age
case friends
}
struct Friend: Codable {
let name: String
}
}
extension User: Encodable {
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(age, forKey: .age)
try container.encode(friends, forKey: .friends)
}
}
extension User: Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
age = try values.decode(Int.self, forKey: .age)
var friends:[Friend] = []
var array = try values.nestedUnkeyedContainer(forKey: .friends)
while(!array.isAtEnd) {
let info = try array.decode(Friend.self)
friends.append(info)
}
self.friends = friends
}
}
ネストしている要素を解析する場合
こんなJSONの場合
{
"name" : "なまえ",
"age" : 21,
"details" : {
"address" : "渋谷区渋谷"
}
}
struct User {
let name: String
let age: Int
let address: String
enum CodingKeys: String, CodingKey {
case name
case age
case details
}
}
extension User: Encodable {
private enum DetailKeys: String, CodingKey {
case address
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(name, forKey: .name)
try container.encode(age, forKey: .age)
var details = container.nestedContainer(keyedBy: DetailKeys.self, forKey: .details)
try details.encode(address, forKey: .address)
}
}
extension User: Decodable {
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
name = try values.decode(String.self, forKey: .name)
age = try values.decode(Int.self, forKey: .age)
let details = try values.nestedContainer(keyedBy: DetailKeys.self, forKey: .details)
address = try details.decode(String.self, forKey: .address)
}
}