You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Swift 4.2中如何将继承类转换为JSON格式?

How to Encode Swift Subclasses to Match Your Target JSON Structure

Hey there! Let's work through this problem together. The main issue you're hitting is that Swift's JSONEncoder doesn't automatically handle subclass properties by default—when you store a subclass instance in an array of the base class type, it only encodes the properties defined in the base class. Let's fix this and generate your desired JSON output.

First, let's recap your target JSON for reference:

{
  "title": "newSurvey",
  "pages": [
    {
      "questions": [
        {
          "title": "Untitled Question",
          "type": "QUESTION_SLIDER",
          "content": {
            "min": "1",
            "max": "10",
            "step": "3",
            "defaultValue": "5"
          }
        },
        {
          "title": "asdasddfdf",
          "type": "QUESTION_TEXT",
          "choices": ["choice1","choice2"]
        }
      ]
    }
  ]
}

Swift enums with associated values are a cleaner, safer way to handle polymorphic data like different question types, compared to class inheritance. Here's how to implement it:

import Foundation

// Define question types to avoid hardcoded strings
enum QuestionType: String, Encodable {
    case slider = "QUESTION_SLIDER"
    case text = "QUESTION_TEXT"
}

// Slider-specific content structure
struct SliderContent: Encodable {
    let min: String
    let max: String
    let step: String
    let defaultValue: String
}

// Enum representing all possible question types
enum Question: Encodable {
    case slider(title: String, content: SliderContent)
    case text(title: String, choices: [String])
    
    // Custom encoding to match your JSON structure
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        
        switch self {
        case .slider(let title, let content):
            try container.encode(title, forKey: .title)
            try container.encode(QuestionType.slider.rawValue, forKey: .type)
            try container.encode(content, forKey: .content)
        case .text(let title, let choices):
            try container.encode(title, forKey: .title)
            try container.encode(QuestionType.text.rawValue, forKey: .type)
            try container.encode(choices, forKey: .choices)
        }
    }
    
    // Coding keys for JSON properties
    private enum CodingKeys: String, CodingKey {
        case title, type, content, choices
    }
}

// Page structure containing questions
struct Page: Encodable {
    let questions: [Question]
}

// Top-level survey structure
struct Survey: Encodable {
    let title: String
    let pages: [Page]
    
    // Convert survey to pretty-printed JSON string
    func toJSONString() -> String? {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        do {
            let data = try encoder.encode(self)
            return String(data: data, encoding: .utf8)
        } catch {
            print("Encoding error: \(error)")
            return nil
        }
    }
}

Usage Example

// Create slider question
let sliderContent = SliderContent(min: "1", max: "10", step: "3", defaultValue: "5")
let sliderQuestion = Question.slider(title: "Untitled Question", content: sliderContent)

// Create text question
let textQuestion = Question.text(title: "asdasddfdf", choices: ["choice1", "choice2"])

// Build survey and generate JSON
let page = Page(questions: [sliderQuestion, textQuestion])
let survey = Survey(title: "newSurvey", pages: [page])

if let jsonString = survey.toJSONString() {
    print(jsonString)
}

Solution 2: Use Class Inheritance (If You Prefer)

If you want to stick with class-based inheritance, you need to override the encode(to:) method in both the base class and subclasses to ensure all properties are encoded:

import Foundation

enum QuestionType: String, Encodable {
    case slider = "QUESTION_SLIDER"
    case text = "QUESTION_TEXT"
}

// Base Question class with custom encoding
class Question: Encodable {
    var title: String
    let type: QuestionType
    
    init(title: String, type: QuestionType) {
        self.title = title
        self.type = type
    }
    
    // Required for Decodable (we're skipping decoding here)
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
    
    // Encode base class properties
    func encode(to encoder: Encoder) throws {
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(title, forKey: .title)
        try container.encode(type.rawValue, forKey: .type)
    }
    
    enum CodingKeys: String, CodingKey {
        case title, type, content, choices
    }
}

// Slider Question subclass
class SliderQuestion: Question {
    var content: SliderContent
    
    init(title: String, content: SliderContent) {
        self.content = content
        super.init(title: title, type: .slider)
    }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
    
    // Override to encode subclass properties
    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder) // Encode base properties first
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(content, forKey: .content)
    }
}

struct SliderContent: Encodable {
    let min: String
    let max: String
    let step: String
    let defaultValue: String
}

// Text Question subclass
class TextQuestion: Question {
    var choices: [String]
    
    init(title: String, choices: [String]) {
        self.choices = choices
        super.init(title: title, type: .text)
    }
    
    required init(from decoder: Decoder) throws {
        fatalError("init(from:) has not been implemented")
    }
    
    // Override to encode subclass properties
    override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder) // Encode base properties first
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(choices, forKey: .choices)
    }
}

// Page and Survey structures
struct Page: Encodable {
    let questions: [Question]
}

class Survey: Encodable {
    let title: String
    let pages: [Page]
    
    init(title: String, pages: [Page]) {
        self.title = title
        self.pages = pages
    }
    
    func toJSONString() -> String? {
        let encoder = JSONEncoder()
        encoder.outputFormatting = .prettyPrinted
        do {
            let data = try encoder.encode(self)
            return String(data: data, encoding: .utf8)
        } catch {
            print("Encoding error: \(error)")
            return nil
        }
    }
}

Usage Example

let sliderContent = SliderContent(min: "1", max: "10", step: "3", defaultValue: "5")
let sliderQuestion = SliderQuestion(title: "Untitled Question", content: sliderContent)

let textQuestion = TextQuestion(title: "asdasddfdf", choices: ["choice1", "choice2"])

let page = Page(questions: [sliderQuestion, textQuestion])
let survey = Survey(title: "newSurvey", pages: [page])

if let jsonString = survey.toJSONString() {
    print(jsonString)
}

Why Your Original Code Didn't Work

When you stored SliderQuestion instances in a [Question] array, JSONEncoder only recognized the base Question type, so it only encoded the title and description properties from the base class—ignoring the content and TYPE properties from the subclass. The solutions above explicitly handle encoding all properties for each question type.

内容的提问来源于stack exchange,提问作者Ammar Alhelali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:27:27