Swift JSON декодер с разными значениями - PullRequest
1 голос
/ 27 марта 2020

Я хочу расшифровать словарь с разными значениями. Таким образом, хотя ключ всегда будет иметь тип String, значение будет одинаковым superclass (например, Shape), но может состоять из различных subclasses (например, Rectangle, Circle). Я хочу иметь возможность позже проверить, какой subclass подключен, но до сих пор я могу использовать только декодирование по умолчанию в [ AttachedObject: Shape ].

См. Пример:

enum AttachedObject: String, Codable {
    case chair
    case lamp
    case desk
}

class Shape: Codable {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Rectangle: Shape {
    var width: Double
    var height: Double

    init(name: String, width: Double, height: Double) {
        self.width = width
        self.height = height
        super.init(name: name)
    }

    enum CodingKeys: String, CodingKey {
        case width
        case height
    }

    public override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.height, forKey: .height)
    }

    required public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.width = try values.decode(Double.self, forKey: .width)
        self.height = try values.decode(Double.self, forKey: .height)
        try super.init(from: decoder)
    }
}

class Circle: Shape {
    var radius: Double

    init(name: String, radius: Double) {
        self.radius = radius
        super.init(name: name)
    }

    enum CodingKeys: String, CodingKey {
        case radius
    }

    public override func encode(to encoder: Encoder) throws {
        try super.encode(to: encoder)
        var container = encoder.container(keyedBy: CodingKeys.self)
        try container.encode(self.radius, forKey: .radius)
    }

    required public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        self.radius = try values.decode(Double.self, forKey: .radius)
        try super.init(from: decoder)
    }
}

class MyRoom: Codable {
     public var attachedShapes: [ AttachedObject: Shape ]

     enum CodingKeys: String, CodingKey {
         case attachedShapes
     }

     public func encode(to encoder: Encoder) throws {
         var container = encoder.container(keyedBy: CodingKeys.self)
         try container.encode(self.attachedShapes, forKey: .attachedShapes)
     }

     required public init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        fatalError("// How to handle the decoding part?")
    }
}

1 Ответ

1 голос
/ 27 марта 2020

Я бы go с чем-то вроде этого:

enum ShapeType: String, RawRepresentable, Codable {
    // Required for RawRepresentable
    static var defaultDecoderValue: ShapeType = .circle

    case circle
    case rectangle
}

struct Shape: Codable {
    let name: String
    let width: Double?
    let height: Double?
    let radius: Double?
    let type: ShapeType
}

Тогда вам не нужны никакие пользовательские ключи. Вы всегда можете обратиться к любому из массивов Shape, et c. Вы можете посмотреть на ShapeType, чтобы увидеть, является ли он прямоугольником или кругом. Вы можете сделать их переменными вместо let, если вам нужно их изменить, и вы можете сделать это Class Shape вместо Struct Shape, если вы хотите вместо этого класс.

...