การถอดรหัส JSON ต่างชนิด
จัดการโครงสร้าง JSON แบบพหุรูปและเปลี่ยนแปลงได้
การถอดรหัส JSON ต่างชนิด เป็นบทเรียน Swift Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Swift Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
JSON ต่างรูปแบบคืออะไร
API บางประเภทส่งอาร์เรย์ที่ออบเจ็กต์มีโครงสร้างแตกต่างกัน โดยแยกความแตกต่างด้วยฟิลด์ type การถอดรหัสข้อมูลเหล่านี้ให้เป็นชนิดข้อมูล Swift เดียวจำเป็นต้องตรวจสอบตัวระบุชนิดนั้นก่อน
import Foundation
let json = "[{\"type\":\"text\",\"value\":\"hi\"},{\"type\":\"number\",\"value\":42}]"
print("Each element carries a type discriminator")การสร้างแบบจำลองด้วยชนิดแจกแจง
แบบจำลอง Swift ที่เหมาะสมตามธรรมชาติคือ enum ที่มีค่าที่เกี่ยวข้อง โดยมีหนึ่ง case ต่อหนึ่งโครงสร้าง JSON decoder จะเลือก case ตามตัวระบุชนิด
import Foundation
enum Block {
case text(String)
case number(Int)
}
print("Enum models the variants")การกำหนดคีย์ตัวระบุชนิด
เพิ่ม enum CodingKeys ที่มีฟิลด์ตัวระบุชนิด ซึ่งในที่นี้คือ type รวมถึงคีย์ข้อมูลที่คุณต้องอ่าน
import Foundation
enum CodingKeys: String, CodingKey {
case type
case value
}
print(CodingKeys.type.stringValue, CodingKeys.value.stringValue)การอ่านตัวระบุชนิด
ใน init(from:) ให้ถอดรหัสข้อความ type ก่อน จากนั้นใช้ switch กับค่านั้นเพื่อถอดรหัสข้อมูลของชนิดที่ตรงกัน
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) }การถอดรหัสอาร์เรย์แบบผสม
เมื่อ enum รองรับ Decodable แล้ว การถอดรหัส [Block].self จะจัดการอาร์เรย์ทั้งหมดที่มีโครงสร้างผสมกันได้ด้วยการเรียกเพียงครั้งเดียว
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)การจับคู่รูปแบบของผลลัพธ์
หลังจากถอดรหัสแล้ว ให้ใช้ switch กับ enum เพื่อจัดการแต่ละรูปแบบอย่างปลอดภัยตามชนิดข้อมูล
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)
}
}ข้อมูลที่ซ้อนกัน
เมื่อแต่ละรูปแบบมีข้อมูลที่สมบูรณ์ยิ่งขึ้น ให้ถอดรหัสโครงสร้าง Codable ที่ซ้อนกันสำหรับแต่ละ case แทนการใช้ค่าเพียงค่าเดียว
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)
}การจัดการชนิดข้อมูลที่ไม่รู้จักอย่างเหมาะสม
แทนที่จะส่งข้อผิดพลาดเมื่อตัวระบุชนิดไม่รู้จัก คุณสามารถจับคู่ตัวระบุนั้นกับ case สำรองได้ เพื่อให้ชนิดข้อมูลใหม่จากเซิร์ฟเวอร์ไม่ทำให้ไคลเอ็นต์หยุดทำงาน
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") }การเลือกตัวระบุชนิดที่ดี
ตัวระบุชนิดควรเป็นฟิลด์ที่มีความคงที่และจำเป็นต้องมี enum String ที่รวมชื่อชนิดข้อมูลที่รู้จักจะช่วยให้ switch ครบทุกกรณีและอ่านได้ง่าย
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)การเข้ารหัสค่าหลายรูปแบบ
หากต้องการให้ข้อมูลเดินทางไปกลับได้ ให้ใช้งาน encode(to:) ด้วย โดยเขียนตัวระบุชนิดและข้อมูลของรูปแบบนั้นกลับออกไป
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)!)การประกอบทุกอย่างเข้าด้วยกัน
แบบจำลองหลายรูปแบบที่สมบูรณ์จะถอดรหัสอาร์เรย์แบบผสม ใช้ switch กับแต่ละรูปแบบ และเข้ารหัสกลับเป็นโครงสร้างเดิมได้ ซึ่งเป็นรูปแบบที่แข็งแกร่งสำหรับ 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)ตรวจความเข้าใจอย่างรวดเร็ว: JSON ต่างรูปแบบ
ทดสอบความเข้าใจของคุณเกี่ยวกับการถอดรหัสข้อมูลหลายรูปแบบ
ทบทวน: การถอดรหัส JSON ต่างรูปแบบ
คุณได้เรียนรู้การจัดการข้อมูลหลายรูปแบบแล้ว:
- สร้างแบบจำลองแต่ละรูปแบบเป็น enum ที่มีค่าที่เกี่ยวข้อง
- ใน
init(from:)ให้ถอดรหัสฟิลด์ตัวระบุชนิด จากนั้นใช้ switch เพื่อสร้าง case ที่ถูกต้อง - ถอดรหัสอาร์เรย์แบบผสมด้วย
[Enum].selfและจับคู่รูปแบบของผลลัพธ์ - เพิ่ม case สำรองสำหรับชนิดข้อมูลที่ไม่รู้จัก และใช้งาน
encode(to:)เพื่อให้ข้อมูลเดินทางไปกลับได้
คำถามที่พบบ่อย
บทเรียน “การถอดรหัส JSON ต่างชนิด” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “การถอดรหัส JSON ต่างชนิด” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Swift Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Swift Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “การถอดรหัส JSON ต่างชนิด”
จัดการโครงสร้าง JSON แบบพหุรูปและเปลี่ยนแปลงได้ คุณปฏิบัติ Swift Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Swift Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Swift Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “การถอดรหัส JSON ต่างชนิด” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Swift Academy นี้ได้ไหม
ได้ บทเรียน Swift Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- CodingKeys สำหรับการเปลี่ยนชื่อ
- กลยุทธ์การถอดรหัสคีย์และวันที่
- การเข้ารหัสด้วยตนเองด้วย encode(to:) และ init(from:)
- การถอดรหัส JSON ต่างชนิด