Dekodowanie heterogenicznego JSON
Obsługa polimorficznych i dynamicznych struktur JSON.
Dekodowanie heterogenicznego JSON to bezpłatna lekcja Swift Academy na CoddyKit. To lekcja 4 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Swift Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Swift Academy zawiera 4 lekcji w sumie.
Czym jest heterogeniczny JSON
Niektóre API zwracają tablice, w których obiekty mają różne struktury rozpoznawane na podstawie pola type. Dekodowanie takich danych do jednego typu Swift wymaga najpierw odczytania tego dyskryminatora.
import Foundation
let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
print("Each element carries a type discriminator")Modelowanie za pomocą enumeracji
Naturalnym modelem w Swift jest enumeracja z wartościami powiązanymi — po jednym wariancie dla każdej struktury JSON. Dekoder wybierze wariant na podstawie dyskryminatora.
import Foundation
enum Block {
case text(String)
case number(Int)
}
print("Enum models the variants")Definiowanie klucza dyskryminatora
Należy dodać enumerację CodingKeys zawierającą pole dyskryminatora, w tym przypadku type, oraz potrzebne klucze danych.
import Foundation
enum CodingKeys: String, CodingKey {
case type
case value
}
print(CodingKeys.type.stringValue, CodingKeys.value.stringValue)Odczytywanie dyskryminatora
W init(from:) należy najpierw zdekodować ciąg znaków type, a następnie użyć instrukcji switch, aby zdekodować pasujące dane wariantu.
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) }Dekodowanie mieszanej tablicy
Gdy enumeracja jest zgodna z Decodable, zdekodowanie [Block].self obsługuje całą tablicę różnych struktur w jednym wywołaniu.
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)Dopasowywanie wyniku za pomocą wzorców
Po dekodowaniu można użyć instrukcji switch dla enumeracji, aby w bezpieczny typowo sposób obsłużyć każdy wariant.
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)
}
}Zagnieżdżone dane wariantów
Gdy warianty zawierają bogatsze dane, należy zdekodować dla każdego z nich zagnieżdżoną strukturę Codable zamiast pojedynczej wartości.
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)
}Łagodne obsługiwanie nieznanych typów
Zamiast zgłaszać błąd dla nieznanych dyskryminatorów, można mapować je na wariant awaryjny, dzięki czemu nowe typy serwera nigdy nie spowodują awarii klienta.
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") }Wybór dobrego dyskryminatora
Dyskryminator powinien być stabilnym i wymaganym polem. Enumeracja String zawierająca znane nazwy typów sprawia, że instrukcja switch jest wyczerpująca i czytelna.
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)Kodowanie wartości polimorficznych
Aby obsłużyć pełny cykl kodowania i dekodowania, należy zaimplementować także encode(to:): zapisać dyskryminator oraz dane wariantu.
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)!)Połączenie wszystkich elementów
Pełny model polimorficzny dekoduje mieszaną tablicę, wybiera wariant każdej wartości i może zakodować dane z powrotem w tej samej strukturze — to solidny wzorzec dla elastycznych API.
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)Szybki test: heterogeniczny JSON
Proszę sprawdzić zrozumienie dekodowania polimorficznego.
Podsumowanie: dekodowanie heterogenicznego JSON
Poznali Państwo sposób obsługi danych polimorficznych:
- Warianty należy modelować jako enumerację z wartościami powiązanymi.
- W
init(from:)należy zdekodować pole dyskryminatora, a następnie użyć instrukcji switch, aby utworzyć właściwy wariant. - Mieszane tablice można dekodować za pomocą
[Enum].self, a wyniki dopasowywać za pomocą wzorców. - Dla nieznanych typów należy dodać wariant awaryjny, a aby obsłużyć pełny cykl, zaimplementować
encode(to:).
Ucz się Swift dzięki korepetycjom AI — za darmo
Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.
- Kursy
- 122
- Lekcje
- 409
Często zadawane pytania
Czy lekcja „Dekodowanie heterogenicznego JSON” jest bezpłatna?
Tak — pełny tekst „Dekodowanie heterogenicznego JSON” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Swift Academy, przejdź na CoddyKit PRO. Kurs Swift Academy zawiera 4 lekcji w sumie.
Co nauczysz się w „Dekodowanie heterogenicznego JSON”?
Obsługa polimorficznych i dynamicznych struktur JSON. Ćwiczysz Swift Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Swift Academy?
Nie wymagamy żadnego doświadczenia. Swift Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 4 z 4.
Ile czasu zajmuje lekcja „Dekodowanie heterogenicznego JSON”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Swift Academy?
Tak. Każda lekcja Swift Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.
Wszystkie lekcje w tym kursie
- CodingKeys do zmiany nazw
- Strategie dekodowania kluczy i dat
- Ręczne encode(to:) i init(from:)
- Dekodowanie heterogenicznego JSON