728x90
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
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")
}
728x90
'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 |
댓글