Swift Academy · leksjon

Dekoding av heterogen JSON

Håndter polymorfe og dynamiske JSON-strukturer.

Leksjon 4 av 413 trinn

Dekoding av heterogen JSON er en gratis leksjon i Swift Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Swift Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Swift Academy inneholder totalt 4 leksjoner.

Hva er heterogen JSON?

Noen API-er returnerer arrayer der objektene har ulike strukturer, identifisert av et type-felt. For å dekode disse til én Swift-type må du først undersøke denne diskriminatoren.

import Foundation

let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
print("Each element carries a type discriminator")

Modellere med en enum

En naturlig Swift-modell er en enum med tilknyttede verdier – én case for hver JSON-struktur. Dekoderen velger riktig case basert på diskriminatoren.

import Foundation

enum Block {
    case text(String)
    case number(Int)
}

print("Enum models the variants")

Definere diskriminatornøkkelen

Legg til en CodingKeys-enum som inneholder diskriminatorfeltet (her type) samt payload-nøklene du trenger å lese.

import Foundation

enum CodingKeys: String, CodingKey {
    case type
    case value
}

print(CodingKeys.type.stringValue, CodingKeys.value.stringValue)

Lese diskriminatoren

I init(from:) dekoder du først type-strengen og bruker deretter en switch på den for å dekode den samsvarende payloaden.

import Foundation

enum Block: Decodable {
    case text(String)
    case number(Int)
    enum CodingKeys: String, CodingKey { case type, value }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        let type = try c.decode(String.self, forKey: .type)
        switch type {
        case "text": self = .text(try c.decode(String.self, forKey: .value))
        case "number": self = .number(try c.decode(Int.self, forKey: .value))
        default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown type")
        }
    }
}

let b = try JSONDecoder().decode(Block.self, from: "{\"type\":\"text\",\"value\":\"hi\"}".data(using: .utf8)!)
if case let .text(s) = b { print(s) }

Dekode en blandet array

Når enum-en er Decodable, håndterer dekoding av [Block].self en hel array med blandede strukturer i ett kall.

import Foundation

enum Block: Decodable {
    case text(String)
    case number(Int)
    enum CodingKeys: String, CodingKey { case type, value }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        switch try c.decode(String.self, forKey: .type) {
        case "text": self = .text(try c.decode(String.self, forKey: .value))
        case "number": self = .number(try c.decode(Int.self, forKey: .value))
        default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown")
        }
    }
}

let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
let blocks = try JSONDecoder().decode([Block].self, from: json.data(using: .utf8)!)
print(blocks.count)

Mønstergjenkjenne resultatet

Etter dekodingen kan du bruke switch på enum-en for å håndtere hver variant på en typesikker måte.

import Foundation

enum Block: Decodable {
    case text(String)
    case number(Int)
    enum CodingKeys: String, CodingKey { case type, value }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        switch try c.decode(String.self, forKey: .type) {
        case "text": self = .text(try c.decode(String.self, forKey: .value))
        case "number": self = .number(try c.decode(Int.self, forKey: .value))
        default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown")
        }
    }
}

let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
for b in try JSONDecoder().decode([Block].self, from: json.data(using: .utf8)!) {
    switch b {
    case .text(let s): print("text:", s)
    case .number(let n): print("number:", n)
    }
}

Nestede payloader

Når variantene inneholder mer omfattende data, dekoder du en nestet Codable-struktur for hvert case i stedet for én enkelt verdi.

import Foundation

struct ImagePayload: Decodable { var url: String; var width: Int }
enum Block: Decodable {
    case image(ImagePayload)
    enum CodingKeys: String, CodingKey { case type }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        switch try c.decode(String.self, forKey: .type) {
        case "image": self = .image(try ImagePayload(from: decoder))
        default: throw DecodingError.dataCorruptedError(forKey: .type, in: c, debugDescription: "unknown")
        }
    }
}

let json = "{\"type\":\"image\",\"url\":\"a.png\",\"width\":100}"
if case let .image(p) = try JSONDecoder().decode(Block.self, from: json.data(using: .utf8)!) {
    print(p.url, p.width)
}

Håndtere ukjente typer på en robust måte

I stedet for å kaste en feil for ukjente diskriminatorer kan du tilordne dem til en reserve-case, slik at nye servertyper aldri får klienten til å krasje.

import Foundation

enum Block: Decodable {
    case text(String)
    case unknown
    enum CodingKeys: String, CodingKey { case type, value }
    init(from decoder: Decoder) throws {
        let c = try decoder.container(keyedBy: CodingKeys.self)
        switch try c.decode(String.self, forKey: .type) {
        case "text": self = .text(try c.decode(String.self, forKey: .value))
        default: self = .unknown
        }
    }
}

let b = try JSONDecoder().decode(Block.self, from: "{\"type\":\"video\"}".data(using: .utf8)!)
if case .unknown = b { print("fell back to unknown") }

Velge en god diskriminator

Diskriminatoren bør være et stabilt og obligatorisk felt. En String-enum med kjente typenavn gjør switch-uttrykket uttømmende og lett å lese.

import Foundation

enum Kind: String, Decodable { case text, number }

let data = "\"text\"".data(using: .utf8)!
let k = try JSONDecoder().decode(Kind.self, from: data)
print(k == .text)

Kode polymorfe verdier

For å kunne gjøre en tur-retur-konvertering implementerer du også encode(to:): skriv diskriminatoren og variantens payload tilbake.

import Foundation

enum Block: Encodable {
    case text(String)
    case number(Int)
    enum CodingKeys: String, CodingKey { case type, value }
    func encode(to encoder: Encoder) throws {
        var c = encoder.container(keyedBy: CodingKeys.self)
        switch self {
        case .text(let s):
            try c.encode("text", forKey: .type)
            try c.encode(s, forKey: .value)
        case .number(let n):
            try c.encode("number", forKey: .type)
            try c.encode(n, forKey: .value)
        }
    }
}

let data = try JSONEncoder().encode(Block.number(42))
print(String(data: data, encoding: .utf8)!)

Sette alt sammen

En fullstendig polymorf modell dekoder en blandet array, bruker switch på hver variant og kan kode tilbake til den samme strukturen – et robust mønster for fleksible API-er.

import Foundation

enum Block: Codable {
    case text(String)
    case number(Int)
    enum CodingKeys: String, CodingKey { case type, value }
    init(from d: Decoder) throws {
        let c = try d.container(keyedBy: CodingKeys.self)
        switch try c.decode(String.self, forKey: .type) {
        case "text": self = .text(try c.decode(String.self, forKey: .value))
        default: self = .number(try c.decode(Int.self, forKey: .value))
        }
    }
    func encode(to e: Encoder) throws {
        var c = e.container(keyedBy: CodingKeys.self)
        switch self {
        case .text(let s): try c.encode("text", forKey: .type); try c.encode(s, forKey: .value)
        case .number(let n): try c.encode("number", forKey: .type); try c.encode(n, forKey: .value)
        }
    }
}

let blocks: [Block] = [.text("hi"), .number(7)]
let data = try JSONEncoder().encode(blocks)
print(try JSONDecoder().decode([Block].self, from: data).count)

Kort kontroll: Heterogen JSON

Test forståelsen din av polymorf dekoding.

Oppsummering: Dekoding av heterogen JSON

Du har lært å håndtere polymorfe payloader:

  • Modeller varianter som en enum med tilknyttede verdier.
  • I init(from:) dekoder du diskriminatorfeltet og bruker deretter switch for å bygge riktig case.
  • Dekoder blandede arrayer med [Enum].self, og mønstergjenkjenn resultatene.
  • Legg til en reserve-case for ukjente typer, og implementer encode(to:) for å kunne gjøre en tur-retur-konvertering.
Gratis å komme i gang

Lær deg Swift med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
122
Leksjoner
409

Ofte stilte spørsmål

Er leksjonen «Dekoding av heterogen JSON» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Swift Academy, inkludert «Dekoding av heterogen JSON», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Swift Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Dekoding av heterogen JSON»?

Håndter polymorfe og dynamiske JSON-strukturer. Du øver på Swift Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Swift Academy?

Ingen tidligere erfaring er nødvendig. Swift Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Dekoding av heterogen JSON»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Swift Academy-leksjonen?

Ja. Alle Swift Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. CodingKeys for navneendring
  2. Strategier for nøkkel- og datodekoding
  3. Manuell encode(to:) og init(from:)
  4. Dekoding av heterogen JSON
← Tilbake til Swift Academy