JSON in Swift kodieren und dekodieren

von @ralfebert · aktualisiert am 20. November 2021
Xcode 13 & iOS 15
Entwickler*innen ohne iOS-Vorkenntnisse
Deutsch

JSON-Daten

Das JSON-Format (JavaScript Object Notation) eignet sich aufgrund seiner einfach lesbaren Textform sehr gut für den Austausch strukturierter Daten. Dabei werden Daten über assoziative Arrays {...} und Listen [...] repräsentiert:

[
    {
        "name": "Bob",
        "age": 32,
        "phone": [
            { "type": "mobile", "number": "0123-456789" },
            { "type": "work", "number": "040-456789" }
        ]
    },
    {
        "name": "Alice",
        "age": 56,
        "address": {
            "street": "Musterstrasse 12",
            "zip": "20095",
            "city": "Hamburg"
        }
    }    
]

JSON-Daten in Swift verarbeiten

Mit den Foundation-Klassen JSONDecoder und JSONEncoder können JSON-Daten in Swift geparst bzw. Swift-Werte als JSON ausgegeben werden. Dabei empfiehlt es sich, Datentypen als struct zu definieren, die 1:1 der JSON-Struktur entsprechen. Diese müssen konform zu dem Codable-Protokoll deklariert werden:

import Foundation

struct Person: Codable {
    let name: String
    let age: Int
    let phone: [Phone]?
    let address: Address?
}

struct Address: Codable {
    let street, zip, city: String
}

struct Phone: Codable {
    let type, number: String
}

Eine JSON-Datei, die im Xcode-Projekt enthalten ist und mit der App ausgeliefert wird, könnte folgendermaßen geparst werden:

if let jsonURL = Bundle.main.url(forResource: "persons", withExtension: "json") {
    let jsonData = try Data(contentsOf: jsonURL)
    let jsonDecoder = JSONDecoder()
let persons = try jsonDecoder.decode([Person].self, from: jsonData)
}

Ein Codable-Objekt kann folgendermaßen als JSON kodiert werden:

let jsonEncoder = JSONEncoder()
let jsonResultData = try jsonEncoder.encode(persons)

Namen der Eigenschaften anpassen, zusätzliche Eigenschaften

Mit einem CodingKeys-Enum können die Namen der Eigenschaften explizit festgelegt werden. Dazu ist es erforderlich, alle Eigenschaften aufzunehmen, die in der JSON-Ausgabe enthalten sein sollen. Eigenschaften die nicht in CodingKeys enthalten sind, werden ignoriert. So lassen sich Eigenschaften in einem Typ aufnehmen, die nicht mit kodiert werden sollen:

struct Person: Codable {
    let name: String
    let age: Int
    var someOtherAttribute: String?

    enum CodingKeys: String, CodingKey {
    case name = "name"
    case age = "ageYears"
}
}

Benennung von Eigenschaften: Snake-case vs. Camel-case

Eigenschaften in JSON-Daten folgen oft der Snake-case-Namenskonvention (z.B. phone_number). Den Swift-Namenskonventionen folgend würde eine entsprechende Eigenschaft nach Camel-case-Namenskonvention (z.B. phoneNumber) benannt werden. Praktischerweise kann eine entsprechende Konvertierung der Namenskonventionen konfiguriert werden:

jsonDecoder.keyDecodingStrategy = .convertFromSnakeCase
jsonEncoder.keyEncodingStrategy = .convertToSnakeCase

Formatierte Ausgabe

Mit outputFormatting kann die Ausgabe der JSON-Serialisierung konfiguriert werden:

jsonEncoder.outputFormatting = .prettyPrinted

Datumswerten

Für die in JSON nicht-standardisierte Kodierung von Datumswerten kann eine entsprechende Option gesetzt werden:

jsonEncoder.dateEncodingStrategy = .iso8601
jsonDecoder.dateDecodingStrategy = .iso8601

Kodierungslogik anpassen

Wenn sich die Struktur des Swift-Typs von seiner kodierten Form unterscheidet, besteht die Möglichkeit, eine eigene Kodierungs- und Dekodierungslogik zu definieren:

Die Anpassung der Kodierungslogik ist nur bis zu einem gewissen Maß sinnvoll. Für sehr unregelmäßig strukturierte JSON-Datenformate könnte die Klasse JSONSerialization eine Alternative sein. Diese mappt nicht auf Swift-Typen, sondern liefert die Werte als Dictionary zurück. Eine weitere Möglichkeit, mit solchen Daten umzugehen ist die Verwendung des Open-Source-Frameworks SwiftyJSON, welches ein komfortables API für den dynamischen Zugriff auf JSON-Daten bereitstellt.

Tools

Codable-Typen generieren

Mit quicktype kann Swift-Code zur Typdefinition anhand von Beispiel-JSON-Daten generiert werden:

Dies kann auch per Kommandozeilentool erfolgen, zum Beispiel:

brew install quicktype
quicktype https://www.ralfebert.de/examples/v3/countries.json --lang swift --no-initializers --no-coding-keys --density normal --acronym-style camel

JSON-Daten strukturiert anzeigen

Mit den Plug-ins JSONView für Chrome/Firefox oder SimplyJSON für Safari können JSON-Daten im Browser formatiert angezeigt werden:

Weiter geht's

→ Tutorial und Beispielprojekt: JSON-Daten mit URLSession laden und mit SwiftUI anzeigen