본문 바로가기
Swift

[Swift] JSON Decoding, Encoding

by Sky Titan 2022. 9. 5.
728x90
 

[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")
}

 

728x90

댓글