0Pricing
Swift Academy · درس

فك ترميز JSON غير المتجانس

تعامل مع أشكال JSON متعددة الأنواع وديناميكية.

فك ترميز JSON غير المتجانس درس مجاني في Swift Academy على CoddyKit. هذا هو الدرس 4 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Swift Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Swift Academy 4 دروس في المجموع.

ما هو JSON غير المتجانس؟

تعيد بعض واجهات برمجة التطبيقات مصفوفات تحتوي على كائنات ذات أشكال مختلفة، ويُميَّز بينها باستخدام حقل type. ويتطلب فك ترميز هذه الكائنات في نوع Swift واحد فحص هذا الحقل المميِّز أولاً.

import Foundation

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

نمذجة البيانات باستخدام تعداد

النموذج الطبيعي في Swift هو تعداد يحتوي على قيم مرتبطة، مع حالة واحدة لكل شكل من أشكال JSON. وستختار وحدة فك الترميز الحالة استناداً إلى الحقل المميِّز.

import Foundation

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

print("Enum models the variants")

تعريف مفتاح التمييز

أضيفوا تعداد 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) }

فك ترميز مصفوفة مختلطة

بعد أن يصبح التعداد 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 على التعداد للتعامل مع كل نوع بطريقة آمنة من حيث النوع.

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 متداخلة لكل حالة بدلاً من قيمة واحدة.

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

التعامل السلس مع الأنواع غير المعروفة

بدلاً من إطلاق خطأ عند مواجهة حقول تمييز غير معروفة، يمكنكم تحويلها إلى حالة احتياطية، بحيث لا تتسبب الأنواع الجديدة على الخادم في تعطل العميل.

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

اختيار حقل تمييز مناسب

ينبغي أن يكون حقل التمييز ثابتاً ومطلوباً. ويساعد تعداد 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 على كل نوع، ويمكنه إعادة الترميز إلى الشكل نفسه، وهو نمط متين لواجهات برمجة التطبيقات المرنة.

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 غير المتجانس

تعلّمتم كيفية التعامل مع البيانات متعددة الأشكال:

  • نمذجوا الأنواع المختلفة كتعداد ذي قيم مرتبطة.
  • في init(from:)، فكّوا ترميز حقل التمييز، ثم استخدموا switch لإنشاء الحالة المناسبة.
  • فكّوا ترميز المصفوفات المختلطة باستخدام [Enum].self وطابقوا أنماط النتائج.
  • أضيفوا حالة احتياطية للأنواع غير المعروفة، ونفّذوا encode(to:) لإجراء دورة ذهاب وإياب كاملة.

الأسئلة الشائعة

هل درس «فك ترميز JSON غير المتجانس» مجاني؟

نعم — نص درس «فك ترميز JSON غير المتجانس» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Swift Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Swift Academy 4 دروس في المجموع.

ماذا ستتعلم في «فك ترميز JSON غير المتجانس»؟

تعامل مع أشكال JSON متعددة الأنواع وديناميكية. تتمرن على Swift Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Swift Academy؟

لا تُشترط خبرة سابقة. Swift Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 4 من أصل 4.

كم من الوقت يستغرق درس «فك ترميز JSON غير المتجانس»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Swift Academy هذا؟

نعم. كل درس في Swift Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. ‏CodingKeys لإعادة التسمية
  2. استراتيجيات فك ترميز المفاتيح والتواريخ
  3. ‏encode(to:) و init(from:) اليدويان
  4. فك ترميز JSON غير المتجانس
← العودة إلى Swift Academy