JSON Decoing with Codable

이전에 작성한 포스팅에서 간단한 JSON을 Codable과 JSONDecoder를 이용해 파싱하였다.

https://wlgusdn700.tistory.com/50?category=913317

 

[Swift] Codable-CodingKey 을 이용해 JSON 파싱하기

Codable이란? A type that can convert itself into and out of an external representation. 자신을 변환하거나 외부 표현(JSON 같은 데이터를 주고 받는 형식)으로 변환할 수 있는 타입이다. 또한 Codable은 De..

wlgusdn700.tistory.com

 

하지만, 프로젝트를 진행하다보면 서버에서 내려준 JSON(Key:Value) 중에 굳이 없어도 되는 값들이 존재한다.

"""
{
	id: 10,
    name: "tree",
    nation: "대한민국"
    ....
}
"""

해당 JSON을 불러오고 여기서 id와 name만 사용한다고 가정하자.

필요없는 Key&Value는 선언하지 않고 Decodable을 채택/준수하면 된다.

 

struct Person: Decodable{
	let id: Int
    let name: String
  
//property 명과 key가 같기 때문에 CodingKeys를 작성하지 않아도 된다.  
//    enum CodingKeys: String, CodingKey{
//    
//    }
}

struct Person2: Decodable{
	let identifier: Int
    let nickname: String
    
    enum CodingKeys: String, CodingKey{
    	case identifier = "id"
        case nickname = "name"
    }
}

 


Nested Decodable

"""
{
	"id": 11,
    "name": "tree",
    "nation": "대한민국",
    "company": {
    	"name": "네카라쿠",
        "industry": "IT",
        ....
    }
    ....
}
"""

위의 JSON이 있다고 가정하자.

만약 내가 필요한 Key&Value가 name, companyIndustry 2가지가 필요하다고 가정하자.

기존의 방식을 사용하면 Person이라는 struct안에 Company라는 struct를 하나 더 두어서 구현하는 방식이 있다.

(물론 해당 방식도 틀리다고 생각하지는 않는다.)

 

struct Person: Decodable{

    let name: String
	let company: Company
    
    struct Company: Decodable{
    	let industry: String
    }
    
}

이렇게 구현 시, industry라는 property에 접근하기 위해서는 

person.company.industry 로 접근해야 한다. 사실 2-depth밖에 되지 않기 때문에, 불편해 보이지는 않는다.

더 심한 예를 들어보자.

 

"""
{
	"httpDomain": "www......",
    "response": {
    	...
        "person":{
        	"id": 29,
            "name": "tree",
            "company": {
                "industry": "IT",
                .....
            }
        }
        .....
    }
}
"""

depth를 한단계 더 추가하였다. (실제 프로젝트에서는 더 깊고, 배열 등의 자료구조까지 들어간다면 훨씬 복잡해진다.)

(나는 name과 industry만 필요한데, 굳이 Response, Person, Company라는 struct를 만들어야하네...? 코드가 너무 길어지는데..?)

 


사실 response.person.company.industry 를 짧게 줄이는 방법은 여러가지가 있을 것이다.

 

1. computed Property

읽기 전용 computedProperty를 선언한다.

struct Response: Decodable{
	....
    var industry: String{
	person.company.industry
    }
    ....
}

 

2. Subcript

배열인 경우에는 서브스크립트를 사용해서 가져올 수 있다.

 

3. init(from decoder: Decoder)

struct Response: Decodable{
.....
	let name: String
    let industry: String

    enum CodingKeys: String, CodingKey{
    	case name
        case response
        case person
        case company
        case industry
    }

    init(from decoder: Decoder) throws{
    //가장 큰 {}가 있는 영역 -> {}를 컨테이너라고 생각하는 것이 이해하기 쉬웠다.
        let values = try decoder.container(keyedBy: CodingKeys.self)
        //response가 차지하고 있는 {}가 있는 영역
        let response = try values.nestedContainer(keyedBy: CodingKeys.self, forKey: .response)
        //response 영역 내에 있는 name을 파싱
        self.name = try response.decode(String.self, forKey: .name)
        //response 영역 내에 있는 person 영역
        let person = try response.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
        //person 영역 내에 있는 company 영역
        let company = try person.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
        //company 영역 내에 있는 industry를 파싱
        self.industry = try company.decode(String.self, forKey: .industry)
        
        //물론 한번에 작성해도 된다.
        
        self.industry = try response.nestedContainer(keyedBy: CodingKeys.self, forKey: .person)
        						.nestedContainer(keyedBy: CodingKeys.self, forKey: .company)
                                .decode(String.self, forKey: .industry)
    }
.....
}

이렇게 작성하게 되면 여러개의 struct를 굳이 만들지 않고도 industry를 최상단 struct에서 사용할 수 있다.

 

또한, String이나 Int 그리고 모든 Key&Value에서 struct에 저장할 때! 변경/조작하고 싶다면 init(from:)에서 조작도 가능하므로

더 폭넓게 사용할 수 있다.

 

+ Recent posts