[Swift] Codable - Decoding 방법
이전 포스트는 Codable의 Encodable에 대해 알아 보았습니다. 이번 포스트는 Codable의 Decodable에 대해 알아보도록 하겠습니다. Decodable JSON 데이터를 디코딩 하는 방법에 대해 알아보도록 하겠습니다. De
jinnify.tistory.com
Decodable을 이용한 JSON 디코딩
import Foundation
extension Data {
func decode<T>(_ type: T.Type) -> T? where T : Decodable {
do {
let result = try JSONDecoder().decode(T.self, from: self)
return result
} catch {
print(error.localizedDescription)
return nil
}
}
}
//디코딩하려는 객체는 Decodable, 혹은 Codable 프로토콜을 채택해야 한다.
class Person: Decodable {
var name: String = "sd"
var age: Int
}
let jsonData: Data = """
{
"name": "Park",
"age": 16
}
""".data(using: .utf8)!
if let person = jsonData.decode(Person.self) {
print(person.name)
}
/*
Park
*/
JSONDecoder를 이용해서 JSON data를 Decodable, Codable 프로토콜을 채택하는 객체로 디코딩한다.
이 때 디코딩하는 코드를 Data extension으로 함수로 만들어 넣으면 편하다.
보통 객체는 Struct로 만들지만 난 class가 익숙해서 class로 만들었다.
필드명이 다른 경우엔 CodingKeys 활용
class Person: Decodable {
var name: String = "sd"
var age: Int
enum CodingKeys: String, CodingKey {
case name = "named"
case age
}
}
let jsonData: Data = """
{
"named": "Park",
"age": 16
}
""".data(using: .utf8)!
if let person = jsonData.decode(Person.self) {
print(person.name)
}
만약 필드명을 다르게 해서 디코딩하고 싶다면, Decodable 객체 안에 CodingKey 프로토콜을 채택하는 CodingKeys라는 enumeration을 선언하고 그 안에 필드들을 선언해주면 된다.
위의 예시에선 "named"라는 json필드를 name이라는 Decodable 객체의 필드로 받아오는 과정이다.
Codable
Apple Developer Documentation
developer.apple.com
typealias Codable = Decodable & Encodable
Codable은 Decodable과 Encodable을 동시채택하는 typealias이다.
즉, JSON 디코딩, 인코딩이 둘 다 가능케하고 싶을 때 채택하면 된다.
Codable을 활용한 JSON Encoding
class Person: Codable {
var name: String
var age: Int
enum CodingKeys: String, CodingKey {
case name = "named"
case age
}
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
var person = Person(name: "Park", age: 15)
if let encoded = try? JSONEncoder().encode(person) {
print(String(data: encoded, encoding: .utf8) ?? "Encoding fail")
}
/*
{"named":"Park","age":15}
*/
Decoding을 JSONDecoder로 했듯이 Encoding은 JSONEncoder로 해주면 된다.
CodingKeys는 encoding을 할 때도 활용되어 name 대신 named라는 필드로 JSON 데이터를 인코딩 해주었다.
아래 예시는 Decodable 객체 안에 Decodable한 필드를 하나 더 넣어서 JSON 뎁스를 더 높인 케이스이고, enumeration에도 Codable을 적용해본 케이스다.
class Person: Codable {
var name: String
var age: Int
var pet: Animal
init(name: String, age: Int, pet: Animal) {
self.name = name
self.age = age
self.pet = pet
}
}
class Animal: Codable {
var name: String
var age: Int
var species: Species
init(name: String, age: Int, species: Species) {
self.name = name
self.age = age
self.species = species
}
}
enum Species: String, Codable {
case dog
case cat
case hamster
}
var pet = Animal(name: "Nabi", age: 2, species: .cat)
var person = Person(name: "Park", age: 15, pet: pet)
if let encoded = try? JSONEncoder().encode(person) {
print(String(data: encoded, encoding: .utf8) ?? "Encoding fail")
}
/*
{"name":"Park","age":15,"pet":{"name":"Nabi","age":2,"species":"cat"}}
*/
참고로 enumeration에 rawValue타입을 String으로 지정해주지 않은 경우엔 아래와 같이 출력되게 된다.
{"name":"Park","age":15,"pet":{"name":"Nabi","age":2,"species":{"cat":{}}}}
다른 Property의 값에 따라 다른 Type으로 Decoding하기
import UIKit
import Foundation
protocol Animal2: Decodable {
}
class Human: Animal2 {
var name: String
}
class Dog: Animal2 {
var name2: String
}
enum Animal {
case human(Human)
case dog(Dog)
}
struct Test: Decodable {
var id: Int
var animal: Animal2
enum CodingKeys: String, CodingKey {
case id
case animal
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.id = try container.decode(Int.self, forKey: .id)
if id == 5454 {
self.animal = try container.decode(Dog.self, forKey: .animal)
} else {
self.animal = try container.decode(Human.self, forKey: .animal)
}
}
}
let jsonData: Data = """
{
"id": 5454,
"animal": {
"name2": "Dogg"
}
}
""".data(using: .utf8)!
if let test = try? JSONDecoder().decode(Test.self, from: jsonData) {
print("sd \((test.animal as? Dog)?.name2)")
} else {
print("else")
}
'Swift' 카테고리의 다른 글
[Swift] Extension에 Stored Property를 못 넣는 이유 (0) | 2022.09.27 |
---|---|
[Swift] Generic where clause 사용 시 '==' vs ':' (0) | 2022.09.25 |
[Swift] Swift Object Lifetime (0) | 2022.08.26 |
[Swift] Weak vs Unowned Reference (0) | 2022.08.20 |
[Swift] 특정 class 상속받아야만 protocol을 채택하도록 제약 걸기 (0) | 2022.07.16 |
댓글