μ€μννΈμ JSON μ§μμ μ¬μ©νμ¬ JSON λ°μ΄ν°μ ꡬ쑰μ κ΄κ³μμ΄ JSON λ°μ΄ν°λ₯Ό μΈμ½λ©νκ³ λμ½λ©ν μ μμ
ννμ μΈ νμ μμ€ν
Swiftβs expressive type system
- Swiftκ° κ°λ ₯ν νμ μΆλ‘ κ³Ό νμ μμ μ±μ μ 곡νλ©°, λ€μν μ νμ λ°μ΄ν°μ μ°μ°μ κ°κ²°νκ³ λͺ ννκ² ννν μ μκ² ν΄μ€
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)")
}
}
Swift API Design Guidelines
λ₯Ό μ μ©ν μ μμ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
κ° νμνμ§ μμΌλ©°, ν΄λΉνλ νλ‘νΌν° μ΄λ¦κ³Ό λμΌν μ΄λ¦μ κ°μ§κ³ μμdecodable
νμ
μ μμ±ν΄μΌν¨decodable
νμ
μ μμ νκ² λμ½λ©ν μ μλ μ€κ° νμ
μν μ νλ©°, λλ¨Έμ§ μ±μμ μ¬μ©ν νμ
μ μ΄λμ
λΌμ΄μ μμ λ°μ΄ν° μμ€ μν μ ν¨import Foundation
struct GroceryStore {
var name: String
var products: [Product]
struct Product: Codable {
var name: String
var points: Int
var description: String?
}
}
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)!
GroceryStore
ꡬ쑰체λ₯Ό μ±μ°λ λ° νμν μ λ³΄λ³΄λ€ λ λ§μ μ 보λ₯Ό ν¬ν¨νκ³ μκ³ , μ νμ ν΅λ‘μ μ λ° λ΄μ μ€μ²©λμ΄ μμDecodable
λ‘ νμν¨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)
}
}
}
}
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)")
}
}
}
Encodable
λ° Decodable
μ νλ‘ν μ½ μꡬμ¬νμ λν μ¬μ©μ μ μ ꡬνμ μμ±νμ¬ JSON ꡬ쑰μ λ€λ₯Έ κΉμ΄μμ λ°μ΄ν°λ₯Ό κ²°ν©νκ±°λ λΆλ¦¬ν μ μμimport Foundation
let json = """
{
"Banana": {
"points": 200,
"description": "A banana grown in Ecuador."
},
"Orange": {
"points": 100
}
}
""".data(using: .utf8)!
key
μ μ΄λ¦κ³Ό λμΌν¨Banana
μ νμ μ λ³΄κ° μ ν μ΄λ¦ μ체μ μ€μ²©λ κ°μ²΄μ μ μ₯λμ΄ μμproduct
ν€μ κ°λ³ μ ν μ΄λ¦μ μ μ₯νλ name
ν€κ° μλ κ²½μ°λ μμ μ μμ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
μ μ€μν¨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:)
- λ°°μ΄ ννμ 컨ν μ΄λμ μ κ·Όν μ μλλ‘ ν΄μ€