Swift Academy · Oppitunti

Heterogeenisen JSONin dekoodaus

Käsittele polymorfisia ja dynaamisia JSON-rakenteita.

Oppitunti 4/413 vaihetta

Heterogeenisen JSONin dekoodaus on ilmainen Swift Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Swift Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Swift Academy-kurssilla on yhteensä 4 oppituntia.

Mitä heterogeeninen JSON tarkoittaa

Jotkin API:t palauttavat taulukoita, joiden objektit ovat rakenteeltaan erilaisia ja joiden tyyppi ilmaistaan type-kentällä. Niiden dekoodaaminen yhdeksi Swift-tyypiksi edellyttää, että tarkistatte ensin tämän erottelukentän.

import Foundation

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

Mallintaminen enumeraatiolla

Luonteva Swift-malli on enumeraatio, jolla on liitettyjä arvoja — yksi tapaus kutakin JSON-rakennetta varten. Dekooderi valitsee tapauksen erottelukentän perusteella.

import Foundation

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

print("Enum models the variants")

Erottelukentän määrittäminen

Lisätkää CodingKeys-enumeraatio, joka sisältää erottelukentän (tässä type) sekä tarvitsemanne hyötykuorman avaimet.

import Foundation

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

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

Erottelukentän lukeminen

Dekoodatkaa init(from:)-metodissa ensin type-merkkijono ja valitkaa sitten sen perusteella oikea hyötykuorma switch-rakenteella.

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) }

Sekamuotoisen taulukon dekoodaus

Kun enumeraatio on Decodable, lauseke [Block].self dekoodaa kokonaisen taulukon erimuotoisia alkioita yhdellä kutsulla.

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)

Tuloksen käsittely pattern matchingilla

Dekoodauksen jälkeen voitte käsitellä jokaista muunnelmaa tyyppiturvallisesti käymällä enumeraation tapaukset läpi switch-rakenteella.

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)
    }
}

Sisäkkäiset hyötykuormat

Kun muunnelmat sisältävät monipuolisempaa dataa, dekoodatkaa jokaista tapausta varten sisäkkäinen Codable-rakenne yksittäisen arvon sijaan.

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)
}

Tuntemattomien tyyppien hallinta hallitusti

Tuntemattomien erottelukenttien aiheuttaman virheen sijaan voitte yhdistää ne varatapahtumaan, jolloin uudet palvelimen tyypit eivät koskaan kaada asiakassovellusta.

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") }

Hyvän erottelukentän valitseminen

Erottelukentän tulisi olla vakaa ja pakollinen kenttä. Tunnettujen tyyppinimien String-enumeraatio pitää switch-rakenteen kattavana ja helposti luettavana.

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)

Polymorfisten arvojen koodaus

Jotta arvon voisi koodata ja dekoodata edestakaisin, toteuttakaa myös encode(to:): kirjoittakaa erottelukenttä ja muunnelman hyötykuorma takaisin.

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)!)

Kokonaisuuden kokoaminen

Täysi polymorfinen malli dekoodaa sekamuotoisen taulukon, käsittelee jokaisen muunnelman switch-rakenteella ja voi koodata datan takaisin samaan muotoon — vankka malli joustaville API-rajapinnoille.

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)

Pikatarkistus: heterogeeninen JSON

Testatkaa, miten hyvin ymmärrätte polymorfista dekoodausta.

Kertaus: heterogeenisen JSON-datan dekoodaus

Opitte käsittelemään polymorfisia hyötykuormia:

  • Mallintakaa muunnelmat enumeraationa, jolla on liitettyjä arvoja.
  • Dekoodatkaa init(from:)-metodissa erottelukenttä ja valitkaa sitten oikea tapaus switch-rakenteella.
  • Dekoodatkaa sekamuotoiset taulukot muodolla [Enum].self ja käsitelkää tuloksia pattern matchingilla.
  • Lisätkää varatapaus tuntemattomille tyypeille ja toteuttakaa encode(to:) edestakaista koodausta varten.
Aloita maksutta

Opi Swift tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
122
Oppitunnit
409

Usein kysytyt kysymykset

Onko oppitunti ”Heterogeenisen JSONin dekoodaus” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Swift Academy-oppimispolun 3 oppituntia, myös oppitunnin “Heterogeenisen JSONin dekoodaus”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Swift Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Heterogeenisen JSONin dekoodaus”?

Käsittele polymorfisia ja dynaamisia JSON-rakenteita. Harjoittelet Swift Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Swift Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Swift Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Heterogeenisen JSONin dekoodaus”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Swift Academy-oppitunnilla?

Kyllä. Jokainen Swift Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. CodingKeys nimien uudelleennimeämiseen
  2. Avainten ja päivämäärien dekoodausstrategiat
  3. Manuaaliset encode(to:)- ja init(from:)-toteutukset
  4. Heterogeenisen JSONin dekoodaus
← Takaisin: Swift Academy