根据属性值类型快速解码对象的 JSON 数组

问题描述 投票:0回答:1

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

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

这里有一些代码基于这个类似的答案,它大部分都完成了,但失败了,因为它不知道CodingKeys的用途

.data

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

谢谢你

json swift codable decodable
1个回答
5
投票

您不需要

data
编码密钥,因为没有“数据”JSON 密钥。只需根据 JSON 字段的值从同一解码器解码
data
属性即可:

struct SchemaObj: Decodable
{

    var dataType: String
    var data: MyData

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

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

如果您计划向该列表添加更多类型,那么

if/else if
可能会变得难以管理,为了改善这种情况,您可以使用查找表来解决此问题:

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)
}
© www.soinside.com 2019 - 2024. All rights reserved.