Swift 根据 属性 值类型解码 JSON 对象数组

Swift decode JSON array of object based on property value type

如何解码不同 JSON 对象的数组,其中每个对象上的相同 属性 告诉您使用什么类型来解码它:

let json =
"""
[
    {
        "@type": "FirstObject",
        "number": 1
    },
    {
        "@type": "SecondObject",
        "name": "myName"
    }
]
"""

这里是一些代码 ,它完成了大部分工作,但失败了,因为它不知道 .data:

的 CodingKeys 是什么
struct FirstObject: MyData {
    var dataType: String
    var number: Int
    
    enum CodingKeys: String, CodingKey {
        case dataType = "@type"
        case number
    }
}

struct SecondObject: MyData {
    var dataType: String
    var name: String
    
    enum CodingKeys: String, CodingKey {
        case dataType = "@type"
        case name
    }
}

struct SchemaObj: Decodable
{
    var dataType: String
    var data: MyData
    
    enum CodingKeys: String, CodingKey {
        case data
        case dataType = "@type"
    }
                
    enum ParseError: Error {
        case UnknownSchemaType(Any)
    }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        dataType = try container.decode(String.self, forKey: .dataType)
        switch dataType {
        case "FirstObject":
            data = try container.decode(FirstObject.self, forKey: .data)
        case "SecondObject":
            data = try container.decode(SecondObject.self, forKey: .data)
        default:
            throw ParseError.UnknownSchemaType(dataType)
        }
    }
}

do {
    let data = Data(json.utf8)
    let result = try JSONDecoder().decode([SchemaObj].self, from: data)
    print(result)
} catch {
    print(error)
}

打印错误为 keyNotFound(CodingKeys(stringValue: "data", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"data\", intValue: nil) (\"data\").", underlyingError: nil))

谢谢

您不需要 data 编码密钥。只需根据 JSON 字段的值从同一解码器解码 data 属性:

enum CodingKeys: String, CodingKey {
    case dataType = "@type"
}

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    dataType = try container.decode(String.self, forKey: .dataType)
    switch dataType {
    case "FirstObject":
        data = try FirstObject(from: decoder)
    case "SecondObject":
        data = try SecondObject(from: decoder)
    default:
        throw ParseError.UnknownSchemaType(dataType)
    }
}

如果您计划向该列表添加更多类型,那么 if/else if 可能会变得难以管理,您可以使用查找 table 来解决此问题:

static let typeMapping: [String: MyData.Type] = [ "FirstObject": FirstObject.self ,
                                                  "SecondObject": SecondObject.self]

init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    let dataType = try container.decode(String.self, forKey: .dataType)
    
    guard let classToDecode = Self.typeMapping[dataType] else {
        throw ParseError.UnknownSchemaType(dataType)
    }
    
    self.dataType = dataType
    self.data = try classToDecode.init(from: decoder)
}