Swift JSON 具有不同值的解码器

Swift JSON decoder with different values

我想解码具有不同值的字典。因此,虽然键始终是 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?")
    }
}

我会选择这样的东西:

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,等等。您可以查看 ShapeType 以查看它是矩形还是圆形。如果您需要更改它们,您可以将它们设为 var 而不是 let,如果您想要 class,则可以设为 is Class Shape 而不是 Struct Shape。