Using JSON with Custom Types πŸ“™

YaR LabΒ·2023λ…„ 6μ›” 25일
0

swiftΒ πŸ“™

λͺ©λ‘ 보기
2/16
post-thumbnail

1️⃣ μ„€λͺ…

μŠ€μœ„ν”„νŠΈμ˜ JSON 지원을 μ‚¬μš©ν•˜μ—¬ JSON λ°μ΄ν„°μ˜ ꡬ쑰에 관계없이 JSON 데이터λ₯Ό μΈμ½”λ”©ν•˜κ³  λ””μ½”λ”©ν•  수 있음

  • λ‹€λ₯Έ μ•±, μ„œλΉ„μŠ€ 및 νŒŒμΌμ—μ„œ λ³΄λ‚΄κ±°λ‚˜ λ°›λŠ” JSON λ°μ΄ν„°λŠ” μ—¬λŸ¬ λ‹€λ₯Έ λͺ¨μ–‘κ³Ό ꡬ쑰λ₯Ό κ°€μ§ˆ 수 있음

2️⃣ μƒ˜ν”Œ

  • 이 μƒ˜ν”Œμ—μ„œ μ„€λͺ…ν•˜λŠ” κΈ°μˆ μ„ μ‚¬μš©ν•˜μ—¬ μ™ΈλΆ€ JSON 데이터와 μ•± λͺ¨λΈ νƒ€μž… κ°„μ˜ 차이λ₯Ό 처리
  • Swift의 ν‘œν˜„μ μΈ νƒ€μž… μ‹œμŠ€ν…œμ„ μ‚¬μš©ν•˜μ—¬ ꡬ쑰가 λ™μΌν•œ κ°μ²΄λ“€μ˜ μ»¬λ ‰μ…˜μ„ μˆ˜λ™μœΌλ‘œ 루프λ₯Ό λŒλ¦¬μ§€ μ•Šκ³  데이터λ₯Ό 읽을 수 있음

    ν‘œν˜„μ μΈ νƒ€μž… μ‹œμŠ€ν…œSwift’s expressive type system

    • Swiftκ°€ κ°•λ ₯ν•œ νƒ€μž… μΆ”λ‘ κ³Ό νƒ€μž… μ•ˆμ „μ„±μ„ μ œκ³΅ν•˜λ©°, λ‹€μ–‘ν•œ μœ ν˜•μ˜ 데이터와 연산을 κ°„κ²°ν•˜κ³  λͺ…ν™•ν•˜κ²Œ ν‘œν˜„ν•  수 있게 ν•΄μ€Œ

πŸ“Œ Read Data From Arrays

  • 이 μ˜ˆμ œμ—μ„œλŠ” κ°’μœΌλ‘œ λ°°μ—΄ νƒ€μž…μ„ μ‚¬μš©ν•˜μ—¬ κ΅¬μ‘°ν™”λœ JSON 데이터λ₯Ό μ²˜λ¦¬ν•˜λŠ” 방법을 μ‚΄νŽ΄λ³Ό 수 있음
  • JSON에 μ•± λͺ¨λΈ νƒ€μž…κ³Ό λ™μΌν•œ νƒ€μž… μš”μ†Œλ‘œ κ΅¬μ„±λœ 배열이 ν¬ν•¨λœ 경우, κ°œλ³„ μš”μ†Œμ˜ νƒ€μž…μ— Codableν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λ„λ‘ 좔가해야함
  • 전체 배열을 λ””μ½”λ“œν•˜κ±°λ‚˜ μΈμ½”λ”©ν•˜λ €λ©΄ [Element].self문법을 μ‚¬μš©
  • μ˜ˆμ œμ—μ„œμ˜ GroceryProductꡬ쑰체λ₯Ό μ„ μ–Έν•  λ•Œ Codableν”„λ‘œν† μ½œμ„μ±„νƒ ν–ˆκΈ° λ•Œλ¬Έμ— GroceryProductκ΅¬μ‘°μ²΄λŠ” μžλ™μœΌλ‘œ λ””μ½”λ”© κ°€λŠ₯
  • 예제의 전체 배열은 decodeλ©”μ„œλ“œ ν˜ΈμΆœμ— μ‚¬μš©λœ ꡬ문에 κΈ°λ°˜ν•˜μ—¬ 디코딩됨
import Foundation

let json = """
[
    {
        "name": "Banana",
        "points": 200,
        "description": "A banana grown in Ecuador."
    },
    {
        "name": "Orange",
        "points": 100
    }
]
""".data(using: .utf8)!

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
}

let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)

print("The following products are available:")
for product in products {
    print("\t\(product.name) (\(product.points) points)")
    if let description = product.description {
        print("\t\t\(description)")
    }
}

πŸ“Œ Change Key Names

  • Swift μ½”λ“œμ—μ„œ μ‚¬μš©ν•˜λŠ” 이름이 JSONμ—μ„œ λ™μΌν•œ 값을 μ°Έμ‘°ν•˜λŠ” 이름과 μΌμΉ˜ν•˜μ§€ μ•Šμ„ 수 있음
  • μ‚¬μš©μž μ •μ˜ 맀핑을 톡해 JSON ν‚€μ˜ 이름과 λ‹€λ₯Έ Swift λͺ¨λΈμ˜ 속성 이름에 Swift API Design Guidelinesλ₯Ό μ μš©ν•  수 있음
  • Swiftμ—μ„œ JSONEncoder 및 JSONDecoder클래슀λ₯Ό μ‚¬μš©ν•  λ•Œ, λ‹€λ₯Έ 이름을 μ‚¬μš©ν•΄μ•Ό ν•˜λŠ” JSON 데이터와도 μ‰½κ²Œ Swift 데이터 νƒ€μž…μ˜ λͺ¨λΈμ˜ 속성 이름을 맀핑할 수 있음
  • 이λ₯Ό μœ„ν•΄ Codable, Encodable, λ˜λŠ” Decodable을 μ€€μˆ˜ν•˜λŠ” λ™μΌν•œ νƒ€μž… 내에 μ€‘μ²©λœ μ—΄κ±°ν˜•μΈ CodingKeysλ₯Ό μ‚¬μš©ν•˜μ—¬ Swift 이름과 JSON 이름 κ°„μ˜ 맀핑을 μ •μ˜ν•¨

    CodingKeys

    • Swift의 Codable ν”„λ‘œν† μ½œμ—μ„œ μ‚¬μš©λ˜λŠ” μ€‘μ²©λœ μ—΄κ±°ν˜•
    • 이 μ—΄κ±°ν˜•μ€ Swift의 속성 이름과 JSON의 ν‚€ 이름 κ°„μ˜ 맀핑을 μ§€μ •ν•˜λŠ” μ—­ν• 
    • String νƒ€μž…μ„ μ‚¬μš©ν•˜λ©°, 각 μΌ€μ΄μŠ€λŠ” 맀핑할 μ†μ„±μ˜ 이름과 ν•΄λ‹Ήν•˜λŠ” JSON ν‚€μ˜ 이름을 λ‚˜νƒ€λƒ„
    • μ—΄κ±°ν˜•μ˜ μΌ€μ΄μŠ€ 이름은 Swift의 속성 이름을 μ‚¬μš©ν•˜κ³ , 각 μΌ€μ΄μŠ€μ—λŠ” ν•΄λ‹Ή 속성에 λ§€ν•‘λ˜λŠ” JSON ν‚€μ˜ 이름을 ν• λ‹Ή
  • 이 μ˜ˆμ œμ—μ„œλŠ” property인 name, points의 Swift 이름이 product_name, product_costμ΄λΌλŠ” JSON 이름과 λ§€ν•‘λ˜λŠ” 방법을 λ³΄μ—¬μ€Œ
import Foundation

let json = """
[
    {
        "product_name": "Bananas",
        "product_cost": 200,
        "description": "A banana grown in Ecuador."
    },
    {
        "product_name": "Oranges",
        "product_cost": 100,
        "description": "A juicy orange."
    }
]
""".data(using: .utf8)!

struct GroceryProduct: Codable {
    var name: String
    var points: Int
    var description: String?
    
    private enum CodingKeys: String, CodingKey {
        case name = "product_name"
        case points = "product_cost"
        case description
    }
}

let decoder = JSONDecoder()
let products = try decoder.decode([GroceryProduct].self, from: json)

print("The following products are available:")
for product in products {
    print("\t\(product.name) (\(product.points) points)")
    if let description = product.description {
        print("\t\t\(description)")
    }
}
  • 두 ν‘œν˜„ 사이에 descriptionμ΄λΌλŠ” 이름이 μΌκ΄€λ˜μ§€λ§Œ, GroceryProducκ΅¬μ‘°μ²΄μ—μ„œ ν•„μš”ν•œ κ°’μ΄λ―€λ‘œ CodingKeysμ—΄κ±°ν˜•μ— ν¬ν•¨μ‹œν‚΄
  • descriptionμ—΄κ±°ν˜• μΌ€μ΄μŠ€λŠ” rawValueκ°€ ν•„μš”ν•˜μ§€ μ•ŠμœΌλ©°, ν•΄λ‹Ήν•˜λŠ” ν”„λ‘œνΌν‹° 이름과 λ™μΌν•œ 이름을 가지고 있음

πŸ“Œ Access Nested Data

  • μ½”λ“œμ—μ„œ ν•„μš”ν•˜μ§€ μ•Šμ€ JSON의 ꡬ쑰와 데이터λ₯Ό λ¬΄μ‹œν•˜λŠ” 방법
  • μ™ΈλΆ€ μ†ŒμŠ€λ‚˜ κΈ°μ‘΄ 둜컬 ν˜•μ‹μ—μ„œ JSON을 μ‚¬μš©ν•˜λŠ” μ½”λ“œλ₯Ό μž‘μ„±ν•  수 있음
  • μ•±μ—μ„œ λͺ¨λΈλ§ν•˜λŠ” κ°œλ…μ˜ ꡬ쑰와 JSON을 λͺ¨λΈλ§ν•˜λŠ” κ°œλ… 사이에 일관성이 없을 수 있음
  • λ•Œλ‘œλŠ” Swift ν”„λ‘œκ·Έλž¨μ„ μœ„ν•œ 논리적인 데이터 λ²ˆλ“€μ΄ μ‚¬μš©ν•˜λŠ” JSON의 μ—¬λŸ¬ μ€‘μ²©λœ κ°μ²΄λ‚˜ λ°°μ—΄λ‘œ 퍼져 μžˆμ„ 수 있음
  • JSON의 ꡬ쑰적인 격차λ₯Ό κ·Ήλ³΅ν•˜κΈ° μœ„ν•΄ μ½μ–΄λ“€μ΄λŠ” JSON의 ꡬ쑰와 μΌμΉ˜ν•˜λŠ” decodableνƒ€μž…μ„ μž‘μ„±ν•΄μ•Όν•¨
  • decodableνƒ€μž…μ€ μ•ˆμ „ν•˜κ²Œ λ””μ½”λ”©ν•  수 μžˆλŠ” 쀑간 νƒ€μž… 역할을 ν•˜λ©°, λ‚˜λ¨Έμ§€ μ•±μ—μ„œ μ‚¬μš©ν•  νƒ€μž…μ˜ μ΄λ‹ˆμ…œλΌμ΄μ €μ—μ„œ 데이터 μ†ŒμŠ€ 역할을 함
  • 쀑간 νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ μžμ‹ μ˜ μ½”λ“œμ—μ„œ κ°€μž₯ μžμ—°μŠ€λŸ¬μš΄ νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄μ„œλ„ λ‹€μ–‘ν•œ ν˜•νƒœμ˜ μ™ΈλΆ€ JSON과의 ν˜Έν™˜μ„±μ„ μœ μ§€ν•  수 있음
  • μ•„λž˜ μ˜ˆμ œμ—μ„œλŠ” JSONμ—μ„œ μ›ν•˜μ§€ μ•ŠλŠ” 데이터와 ꡬ쑰λ₯Ό κ±΄λ„ˆλ›°κ³  μ‹λ£Œν’ˆ μ œν’ˆμ„ μΆ”μΆœν•˜λŠ” 방법을 보여주기 μœ„ν•΄ 쀑간 νƒ€μž…μ„ μ‚¬μš©
import Foundation

struct GroceryStore {
    var name: String
    var products: [Product]
    
    struct Product: Codable {
        var name: String
        var points: Int
        var description: String?
    }
}
  • APIκ°€ μ‹λ£Œν’ˆμ μ— λŒ€ν•œ 정보λ₯Ό μ•„λž˜μ™€ 같이 κ΅¬μ‘°ν™”λœ JSON으둜 제곡
let json = """
[
    {
        "name": "Home Town Market",
        "aisles": [
            {
                "name": "Produce",
                "shelves": [
                    {
                        "name": "Discount Produce",
                        "product": {
                            "name": "Banana",
                            "points": 200,
                            "description": "A banana that's perfectly ripe."
                        }
                    }
                ]
            }
        ]
    },
    {
        "name": "Big City Market",
        "aisles": [
            {
                "name": "Sale Aisle",
                "shelves": [
                    {
                        "name": "Seasonal Sale",
                        "product": {
                            "name": "Chestnuts",
                            "points": 700,
                            "description": "Chestnuts that were roasted over an open fire."
                        }
                    },
                    {
                        "name": "Last Season's Clearance",
                        "product": {
                            "name": "Pumpkin Seeds",
                            "points": 400,
                            "description": "Seeds harvested from a pumpkin."
                        }
                    }
                ]
            }
        ]
    }
]
""".data(using: .utf8)!
  • APIμ—μ„œ λ°˜ν™˜ν•˜λŠ” JSON은 GroceryStoreꡬ쑰체λ₯Ό μ±„μš°λŠ” 데 ν•„μš”ν•œ 정보보닀 더 λ§Žμ€ 정보λ₯Ό ν¬ν•¨ν•˜κ³  있고, μ œν’ˆμ€ ν†΅λ‘œμ™€ μ„ λ°˜ 내에 μ€‘μ²©λ˜μ–΄ 있음
  • JSON을 μ œκ³΅ν•˜λŠ” 업체(APIλ₯Ό 제곡)λŠ” μΆ”κ°€ 정보가 ν•„μš”ν•  수 μžˆμ§€λ§Œ, 이λ₯Ό μ‚¬μš©ν•˜λŠ” μ•±μ—μ„œλŠ” μœ μš©ν•˜μ§€ μ•Šμ„ 수 있음
  • μ™ΈλΆ€ μ»¨ν…Œμ΄λ„ˆμ—μ„œ ν•„μš”ν•œ 데이터λ₯Ό μΆ”μΆœν•˜κΈ° μœ„ν•΄, JSONκ³Ό λ™μΌν•œ ꡬ쑰λ₯Ό 가진 μœ ν˜•μ„ μž‘μ„±ν•˜κ³  Decodable둜 ν‘œμ‹œν•¨
  • 그런 λ‹€μŒ, λ‚˜λ¨Έμ§€ μ•±μ—μ„œ μ‚¬μš©ν•  μœ ν˜•μ— JSONκ³Ό μœ μ‚¬ν•œ μœ ν˜•μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μ·¨ν•˜λŠ” μ΄λ‹ˆμ…œλΌμ΄μ €λ₯Ό μž‘μ„±
  • μ•„λž˜μ˜ μ˜ˆμ œμ—μ„œ GroceryStoreServiceκ΅¬μ‘°μ²΄λŠ” μ•±μ—μ„œμ˜ JSON μ‚¬μš©μ„ μœ„ν•΄ GroceryStorꡬ쑰체 κ°„μ˜ μ€‘κ°œμž 역할을 함
struct GroceryStoreService: Decodable {
    let name: String
    let aisles: [Aisle]
    
    struct Aisle: Decodable {
        let name: String
        let shelves: [Shelf]
        
        struct Shelf: Decodable {
            let name: String
            let product: GroceryStore.Product
        }
    }
}
  • GroceryStoreServiceκ΅¬μ‘°μ²΄λŠ” JSON의 ꡬ쑰(aisles와 shelves 포함)와 μΌμΉ˜ν•˜κΈ° λ•Œλ¬Έμ—, Decodableν”„λ‘œν† μ½œμ˜ μ€€μˆ˜λŠ” μžλ™μœΌλ‘œ 이루어짐
  • GroceryStoreꡬ쑰체의 μ€‘μ²©λœ Productκ΅¬μ‘°μ²΄λŠ” 데이터가 λ™μΌν•œ 이름과 νƒ€μž…μ„ μ‚¬μš©ν•˜κΈ° λ•Œλ¬Έμ— Shelfκ΅¬μ‘°μ²΄μ—μ„œ μž¬μ‚¬μš©λ¨
  • GroceryStoreServiceꡬ쑰체의 역할을 μ™„λ£Œν•˜κΈ° μœ„ν•΄ GroceryStoreꡬ쑰체에 ν™•μž₯(extension)을 μ‚¬μš©
  • 이 ν™•μž₯은 GroceryStoreServiceμΈμŠ€ν„΄μŠ€λ₯Ό μ·¨ν•˜λŠ” μ΄λ‹ˆμ…œλΌμ΄μ €λ₯Ό μΆ”κ°€ν•˜κ³ , aisleκ³Ό shelf을 μˆœνšŒν•˜λ©΄μ„œ λΆˆν•„μš”ν•œ 쀑첩을 μ œκ±°ν•¨
extension GroceryStore {
    init(from service: GroceryStoreService) {
        name = service.name
        products = []
        
        for aisle in service.aisles {
            for shelf in aisle.shelves {
                products.append(shelf.product)
            }
        }
    }
}
  • νƒ€μž…κ°„μ˜ 관계λ₯Ό ν™œμš©ν•˜λ©΄ μ•ˆμ „ν•˜κ³  κ°„κ²°ν•˜κ²Œ JSON을 μ½μ–΄λ“€μž„
  • GroceryStoreService쀑간 νƒ€μž…μ„ 거쳐 μ΅œμ’…μ μœΌλ‘œ GroceryStoreμΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•  수 있음
let decoder = JSONDecoder()
let serviceStores = try decoder.decode([GroceryStoreService].self, from: json)

let stores = serviceStores.map { GroceryStore(from: $0) }

for store in stores {
    print("\(store.name) is selling:")
    for product in store.products {
        print("\t\(product.name) (\(product.points) points)")
        if let description = product.description {
            print("\t\t\(description)")
        }
    }
}

πŸ“Œ Merge Data at Different Depths

  • Encodable 및 Decodable의 ν”„λ‘œν† μ½œ μš”κ΅¬μ‚¬ν•­μ— λŒ€ν•œ μ‚¬μš©μž μ •μ˜ κ΅¬ν˜„μ„ μž‘μ„±ν•˜μ—¬ JSON ꡬ쑰의 λ‹€λ₯Έ κΉŠμ΄μ—μ„œ 데이터λ₯Ό κ²°ν•©ν•˜κ±°λ‚˜ 뢄리할 수 있음
  • JSON νŒŒμΌμ΄λ‚˜ APIμ—μ„œ μ‚¬μš©ν•˜λŠ” 데이터 λͺ¨λΈμ΄ μ•±μ—μ„œ μ‚¬μš©ν•˜λŠ” λͺ¨λΈκ³Ό μΌμΉ˜ν•˜μ§€ μ•Šμ„ λ•Œκ°€ 있음
  • 이런 경우 인코딩 및 λ””μ½”λ”© μ‹œ JSONμ—μ„œ 객체λ₯Ό λ³‘ν•©ν•˜κ±°λ‚˜ 뢄리해야 ν•  수 있음
  • 결과적으둜 단일 μΈμŠ€ν„΄μŠ€μ˜ 인코딩 λ˜λŠ” 디코딩은 JSON 객체의 계측 κ΅¬μ‘°μ—μ„œ μœ„λ‘œ λ˜λŠ” μ•„λž˜λ‘œ λ„λ‹¬ν•˜λŠ” 것을 포함함
  • μ•„λž˜μ˜ μ˜ˆμ œμ—μ„œλŠ” μ΄λŸ¬ν•œ 데이터 병합 μŠ€νƒ€μΌμ˜ 일반적인 μ˜ˆμ‹œλ₯Ό 보여주고, 각각의 μ œν’ˆμ— λŒ€ν•œ 이름, 가격 및 기타 μ„ΈλΆ€ 정보λ₯Ό μΆ”μ ν•˜λŠ” μ‹λ£Œν’ˆ 점을 λͺ¨λΈλ§ν•¨
import Foundation

let json = """
{
    "Banana": {
        "points": 200,
        "description": "A banana grown in Ecuador."
    },
    "Orange": {
        "points": 100
    }
}
""".data(using: .utf8)!
  • JSONμ—μ„œ μ œν’ˆμ˜ 이름이 μ œν’ˆμ˜ λ‚˜λ¨Έμ§€ μ„ΈλΆ€ 정보λ₯Ό μ •μ˜ν•˜λŠ” key의 이름과 동일함
  • 이 경우 Bananaμ œν’ˆμ˜ 정보가 μ œν’ˆ 이름 μžμ²΄μ— μ€‘μ²©λœ 객체에 μ €μž₯λ˜μ–΄ 있음
  • κ·ΈλŸ¬λ‚˜ μ œν’ˆμ˜ 이름이 객체의 식별 ν‚€μ—μ„œ λ‚˜μ˜¨λ‹€λŠ” 것은 관둀에 μ˜ν•΄λ§Œ λͺ…확함
  • λ‹€λ₯Έ JSON ꡬ쑰의 ν˜•νƒœμ—μ„œλŠ” 각 μ œν’ˆμ— λŒ€ν•΄ product킀와 κ°œλ³„ μ œν’ˆ 이름을 μ €μž₯ν•˜λŠ” nameν‚€κ°€ μžˆλŠ” κ²½μš°λ„ μžˆμ„ 수 있음
  • 이 λŒ€μ²΄ ν˜•νƒœλŠ” Swiftμ—μ„œ 데이터λ₯Ό λͺ¨λΈλ§ν•˜λŠ” 방식과 μΌμΉ˜ν•˜λ©°, μ•„λž˜μ˜ μ˜ˆμ œμ—μ„œ 확인할 수 있음
struct GroceryStore {
    struct Product {
        let name: String
        let points: Int
        let description: String?
    }

    var products: [Product]

    init(products: [Product] = []) {
        self.products = products
    }
}
  • λ‹€μŒμ˜ˆμ œμ—μ„œ ν™•μž₯은 GroceryStoreꡬ쑰체λ₯Ό Encodableν”„λ‘œν† μ½œμ— μ€€μˆ˜ν•˜λ„λ‘ 함
  • 이λ₯Ό 톡해 κ΅¬μ‘°μ²΄λŠ” Codableν”„λ‘œν† μ½œμ„ μ€€μˆ˜ ν•  수 있음
  • 이 ν™•μž₯은 CodingKeyν”„λ‘œν† μ½œμ— μ€€μˆ˜ν•˜λŠ” 일반적인 μ—΄κ±°ν˜• λŒ€μ‹ μ— 쀑첩 ꡬ쑰체인 ProductKeyλ₯Ό μ‚¬μš©
  • μ œν’ˆ ꡬ쑰체의 μΈμŠ€ν„΄μŠ€(이름 μ‚¬μš©)λ₯Ό μ‚¬μš©ν•  λ•Œ 일뢀 κ³ μœ ν•œ 인코딩 ν‚€λ₯Ό μ²˜λ¦¬ν•˜κΈ° μœ„ν•΄ ꡬ쑰체가 ν•„μš”
extension GroceryStore: Encodable {
    struct ProductKey: CodingKey {
        var stringValue: String
        init?(stringValue: String) {
            self.stringValue = stringValue
        }

        var intValue: Int? { return nil }
        init?(intValue: Int) { return nil }

        static let points = ProductKey(stringValue: "points")!
        static let description = ProductKey(stringValue: "description")!
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: ProductKey.self)
        
        for product in products {
            // Any product's `name` can be used as a key name.
            let nameKey = ProductKey(stringValue: product.name)!
            var productContainer = container.nestedContainer(keyedBy: ProductKey.self, forKey: nameKey)
            
            // The rest of the keys use static names defined in `ProductKey`.
            try productContainer.encode(product.points, forKey: .points)
            try productContainer.encode(product.description, forKey: .description)
        }
    }
}
  • Encodableν”„λ‘œν† μ½œμ„ μ€€μˆ˜ν•˜λ―€λ‘œ, μ•„λž˜ μ˜ˆμ œμ—μ„œλŠ” JSONEncoderμΈμŠ€ν„΄μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ μ–΄λ–€ GroceryStoreμΈμŠ€ν„΄μŠ€λ“  JSON으둜 인코딩할 수 있음
var encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

let store = GroceryStore(products: [
    .init(name: "Grapes", points: 230, description: "A mixture of red and green grapes."),
    .init(name: "Lemons", points: 2300, description: "An extra sour lemon.")
])

print("The result of encoding a GroceryStore:")
let encodedStore = try encoder.encode(store)
print(String(data: encodedStore, encoding: .utf8)!)
print()
  • Codableν”„λ‘œν† μ½œ μ€€μˆ˜μ˜ 두 번째 뢀뢄은 λ””μ½”λ”©μž„
  • μ•„λž˜μ˜ ν™•μž₯은 GroceryStoreꡬ쑰체가 Decodable을 μ€€μˆ˜ν•¨
  • JSON 객체λ₯Ό λ””μ½”λ”©ν•˜λŠ” μΌν™˜μœΌλ‘œ, μ΄λ‹ˆμ…œλΌμ΄μ €λŠ” 객체의 첫 번째 λ‹¨κ³„μ˜ λͺ¨λ“  ν‚€λ₯Ό μˆœνšŒν•¨
extension GroceryStore: Decodable {
    public init(from decoder: Decoder) throws {
        var products = [Product]()
        let container = try decoder.container(keyedBy: ProductKey.self)
        for key in container.allKeys {
            // Note how the `key` in the loop above is used immediately to access a nested container.
            let productContainer = try container.nestedContainer(keyedBy: ProductKey.self, forKey: key)
            let points = try productContainer.decode(Int.self, forKey: .points)
            let description = try productContainer.decodeIfPresent(String.self, forKey: .description)

            // The key is used again here and completes the collapse of the nesting that existed in the JSON representation.
            let product = Product(name: key.stringValue, points: points, description: description)
            products.append(product)
        }

        self.init(products: products)
    }
}

let decoder = JSONDecoder()
let decodedStore = try decoder.decode(GroceryStore.self, from: json)

print("The store is selling the following products:")
for product in decodedStore.products {
    print("\t\(product.name) (\(product.points) points)")
    if let description = product.description {
        print("\t\t\(description)")
    }
}

container

  • 일반적으둜 λ””μ½”λ”© μž‘μ—…μ„ μˆ˜ν–‰ν•  λ•Œ, λ””μ½”λ”©λœ λ°μ΄ν„°λŠ” μ»¨ν…Œμ΄λ„ˆ ν˜•νƒœλ‘œ ꡬ성됨
  • JSON 데이터λ₯Ό λ””μ½”λ”©ν•  λ•Œ, JSON κ°μ²΄λŠ” λ”•μ…”λ„ˆλ¦¬ ν˜•νƒœλ‘œ λ””μ½”λ”©λ˜κ³ , JSON 배열은 λ°°μ—΄ ν˜•νƒœλ‘œ 디코딩됨
  • μ•„λž˜μ˜ λ©”μ„œλ“œλ₯Ό 톡해 디코더가 μΈμ½”λ”©λœ 데이터λ₯Ό ν•΄λ…ν•˜κ³  ν•΄λ‹Ή 데이터 μ»¨ν…Œμ΄λ„ˆμ— μ ‘κ·Όν•  수 있음
  • μ•„λž˜μ˜ λ©”μ„œλ“œλ₯Ό 톡해 μ»¨ν…Œμ΄λ„ˆμ— μ ‘κ·Όν•˜μ—¬ λ””μ½”λ”©λœ 데이터λ₯Ό μΆ”μΆœν•˜κ±°λ‚˜ μ²˜λ¦¬ν•  수 μžˆλŠ” κΈ°λŠ₯을 제곡

container(keyedBy:)

  • λ”•μ…”λ„ˆλ¦¬ ν˜•νƒœμ˜ μ»¨ν…Œμ΄λ„ˆμ— μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•΄μ€Œ

container(unkeyed:)

  • λ°°μ—΄ ν˜•νƒœμ˜ μ»¨ν…Œμ΄λ„ˆμ— μ ‘κ·Όν•  수 μžˆλ„λ‘ ν•΄μ€Œ

μΆœμ²˜πŸ“š

🍎Apple Docs: Using JSON with Custom Types

0개의 λŒ“κΈ€