티스토리 뷰
안녕하세요 :) Zedd입니다.
JSONDecoder에 Key decoding strategy가 새로 생겼습니다~~~~
KeyDecodingStrategy는 enum으로, case가 3가지 있습니다.
useDefaultKeys
convertFromSnakeCase
custom(([CodingKey]) -> CodingKey)
이렇게 3가지가 있으며, Default는 useDefaultKeys입니다 :)
하나하나 볼까요?
● useDefaultKeys
디코딩 중에 키 이름을 변경하지 않는 Key decoding strategy입니다.
useDefaultKeys 전략은 지정하지 않으면 사용되는 전략입니다.
끝..
● convertFromSnakeCase
드디어...드디어ㅓㅓㅓㅓㅓㅓㅓㅓㅓㅓㅓ드디어 나온부분
Snake-case(다들 아실거라 믿어요..)를 Camel-case로 변환하는 Key decoding strategy입니다.
Snake-case와 Camel-case는 API의 일부를 명명 할 때 단어를 결합하는 두 가지 일반적인 방법입니다.
Swift API 디자인 가이드 라인에 의하면, Camel-case 이름을 사용하는 것이 좋습니다.
일부 JSON API는 Snake-case를 채택합니다. 그러한 API를 접할 때 이 전략(strategy)을 사용하십시오.
이 전략은 대문자 Letters와 lowercaseLetters를 사용하여 단어 사이의 경계를 결정하고 글자를 대문자로 표시 할 때 시스템 locale을 결정합니다.
이 전략은 JSON 키를 camel-case로 변환하기위한 다음 단계를 따릅니다.
1. 밑줄(_) 뒤에 오는 각 단어를 대문자로합니다.
2. 문자열의 맨 처음이나 끝에 있지 않은 모든 밑줄을 제거합니다.
3. 단어를 하나의 문자열로 결합합니다.
fee_fi_fo_fum
Converts to: fee
feeFiFoFum
Converts to: fee
base_uri
Converts to: base
말로만 하지말고, 예제로 봅시다 ㄱ
struct OlympicEventResult: Codable {
var goldWinner: String
var silverWinner: String
var bronzeWinner: String
}
OlympicEventResult라는 구조체를 하나 만들게요.
그리고, 이제 json을 하나 만들어야 하는데...
let json = """
{
"silver_winner": "Sound",
"gold_winner": "Light",
"bronze_winner": "Unladen Swallow"
}
"""
Snake-case로 만들겠습니다!!!!!!
let decoder = JSONDecoder()
let data = json.data(using: .utf8)
if let data = data, let result = try? decoder.decode(OlympicEventResult.self, from: data) {
print(result.goldWinner)
print(result.silverWinner)
print(result.bronzeWinner)
}
이렇게 하면 결과가 어떻게 될까요?
..아무것도 print되지 않습니다! 왜냐면 아직 strategy를 안줬음 ㅎ
JSONDecoder는 Key이름과 프로퍼티이름이 일치하는 곳에 value를 할당하는데,
key이름은 gold_winner이지만..프로퍼티에는 goldWinner는 있고 gold_winner는 없으니..
저 try?구문에서 실패를 하고 넘어가게 되죠.
하지만...! convertFromSnakeCase를 사용해봅시다.
let decoder = JSONDecoder()
let data = json.data(using: .utf8)
decoder.keyDecodingStrategy = .convertFromSnakeCase
if let data = data, let result = try? decoder.decode(OlympicEventResult.self, from: data) {
print(result.goldWinner)
print(result.silverWinner)
print(result.bronzeWinner)
}
이렇게 Key decoding strategy로 convertFromSnakeCase를 지정해주면..!
이렇게 아주 잘 나오는 것을 볼 수 있습니다 XD..
와 그럼 <왕초보를 위한 Codable - Codingkey>글에서 봤듯이;;; 이제 enum으로 막 CodingKey준수하고 그런 작업 안해줘도 돼?
=> 는 아닙니다.. convertFromSnakeCase가 작동하는 단계가
1. 밑줄(_) 뒤에 오는 각 단어를 대문자로합니다.
2. 문자열의 맨 처음이나 끝에 있지 않은 모든 밑줄을 제거합니다.
3. 단어를 하나의 문자열로 결합합니다.
이렇다고 그랬죠?
만약 이렇게 key와 프로퍼티 이름이, _를 제외하고 “다르다면”
여전히 위 방법으로 enum을 만들어서 해야겠죠.
아무튼 그래도 convertFromSnakeCase가 나와서 넘나 좋은것..
아직 안끝났습니다..!
하나 더 남았어요 :)
● custom(([CodingKey]) -> CodingKey)
정의는 당신이 제공하는 클로저에 의해 정의 된 key decoding strategy.
이 case와 관련된 값은(The value associated with this case) 디코딩된 JSON객체의 key이름을 타입의 CodingKey이름에 매핑하는데 사용되는 클로저 입니다.
디코딩 하는 동안, 디코드 되는 Decodable 값에서, 각 Key에 대해 클로저가 한번 호출됩니다.
클로져는, 디코드 되는 값에 도달하기 위헤서 필요한 일련의 키를 나타내는 CodingKey인스턴스의 배열과 함께 불립니다.
아래 예제는 custom(([CodingKey]) -> CodingKey)을 사용하여 중첩된 A,B 및 C구조의 특성을 디코딩 하는 방법을 보여줍니다.
struct A: Codable {
var value: Int
var b: B
struct B: Codable {
var value: Int
var c: C
struct C: Codable {
var value: Int
}
}
}
오 넘나 복잡한 구조체인것 nested type으로 정의를 했네요.
let json = """
{
"a.value": 1,
"b": {
"a.b.value": 2,
"c": {
"a.b.c.value": 3
}
}
}
""".data(using: .utf8)!
JSON도 역시...엄청난(?) 중첩구조를 가지고 있습니다. 이런거 Codable로 하려면 어케함 ㅡㅡ
ㄱㄷㄱㄷ
struct AnyKey: CodingKey {
var stringValue: String
var intValue: Int?
init?(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
init?(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
갑자기 CodingKey프로토콜을 채택하는 AnyKey라는 구조체를 정의해줬습니다.
CodingKey라는 프로토콜이 요구하는 뭔가가 있나보네요.
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .custom { keys in
let lastComponent = keys.last!.stringValue.split(separator: ".").last!
return AnyKey(stringValue: String(lastComponent))!
}
let a = try? decoder.decode(A.self, from: json)
if let a = a {
print(a.b.c.value) // Prints "3"
}
자, 클로저 안을 주목해주세요.
저 클로저는 각 Key에 대해 한번 호출된다고 했으니..
let json = """
{
"a.value": 1,
"b": {
"a.b.value": 2,
"c": {
"a.b.c.value": 3
}
}
}
""".data(using: .utf8)!
위 JSON에서 Key라고 말할거는 5개가 있죠? 한줄씩..
그럼 5번이 호출될 것 같은 느낌
그러하다 ㅇㅇ
여기서 왜인지는 모르지만, 배열의 가장 마지막에는 JSONKey가 들어가나본데, 우리는 이 JSONKey만 사용해야합니다.
decoder.keyDecodingStrategy = .custom { keys in
let lastComponent = keys.last!.stringValue.split(separator: ".").last!
return AnyKey(stringValue: String(lastComponent))!
}
keys(배열)의 마지막 원소의 stringValue(엥 last에 stringValue라는 프로퍼티가 있음? -> 이 keys는 CodingKey프로토콜 타입 배열이므로 stringValue가 있음 ㅇㅇ)
를 가져와서. 그거를 .을 기준으로 나눈뒤에 split은 배열을 리턴하므로, 또 그 배열의 last를 AnyKey의 이니셜라이저 파라미터로 넣어줍니다.
let a = try? decoder.decode(A.self, from: json)
if let a = a {
print(a.b.c.value) // Prints "3"
}
이렇게 하면..저렇게 복잡한 중첩구조의 JSON도 잘 풀 수 있나보네요.
솔직히 이 내부적인?..구조가 잘 이해가 안가는데...ㅎㅎ
좀 실전에서 쓰면서 알아봐야 할 것 같아요 :)
아무튼 저는 convertFromSnakeCase가 나와줘서 아주 감사한...Apple....Love..
솔직히 지금 Codable을 사용하고 있지는 않지만, 이 convertFromSnakeCase덕분에 나중에 편해질 수도 있으니까요 :)
아무튼 Codable을 사용하시는 분들게 도움이 되는 글이길 바래요 XD
'Swift' 카테고리의 다른 글
Swift 4.1 Released! -2 (0) | 2018.04.14 |
---|---|
Swift ) Hashable (1) | 2018.04.10 |
Swift 4.1 Released! - 1 (0) | 2018.04.07 |
Swift ) NSString.CompareOptions종류 (0) | 2018.03.24 |
Swift ) ComparisonResult살펴보기 (0) | 2018.03.24 |
- WidgetKit
- UIBezierPath
- np-hard
- WKWebView
- github
- swift 공부
- swift3
- Accessibility
- swift delegate
- 스위프트
- Swift
- 제이슨 파싱
- Xcode
- np-complete
- actor
- swift array
- 스위프트 문법
- swift tutorial
- 피아노
- FLUTTER
- Combine
- 회고
- iOS delegate
- SwiftUI
- WWDC
- IOS
- ios 13
- Git
- swift sort
- fastlane
- Total
- Today
- Yesterday