首页 > 解决方案 > 如何使用 Codable 解析不同对象列表的 Json 数组?

问题描述

我有一个带有项目列表的 json 数组,它具有不同的属性。

{
    "items": [ 
        {
            "id": "1",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"

        },
        {
            "id": "2",
            "name": "name",
            "propertyOfA": "12",
            "productype": "a"
        },
        {
            "id": "3",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfB": "1243",
            "productype": "b"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfC": "1243",
            "propertyOfC2": "1243",
            "productype": "c"
        }
        ]
}

我通常做的是将其解析为:


struct RequestData: Codable {
    let items: [Item]
}

enum ProductType: String, Codable {
    case a = "A"
    case b = "B"
    case c = "C"
}

struct Item: Codable {
    let id, name: String
    let propertyOfA: String?
    let productype: String
    let propertyOfB: String?
    let propertyOfC: String?
    let propertyOfC2: String?
}

但是如果这个数组继续增长,它最终会得到一个具有大量可选属性的 Item,所以我希望每个对象都有自己的专用类

在可编码解析中添加某种桥梁,

我要存档的内容类似于:

struct RequestData: Codable {
    let items: Items
}

struct items: Codable {
    let arrayA: [A]
    let arrayB = [B]
    let arrayC = [C]
}

struct A: Codable {
    let id: String,
    let name: String,
    let propertyOfA: String,
    let producttype: Productype
}

struct B {
...
}

struct C {
...
}

我可以用 Codable 做到这一点吗?

标签: swiftdecodable

解决方案


一个合理的解决方案是一个带有关联值的枚举,因为类型可以由productype键确定。该init方法首先productype用 a 解码 aCodingKey然后switch它解码(从 a singleValueContainer)并将正确的类型/值与相应的情况相关联。

enum ProductType: String, Codable {
    case a, b, c
}

struct Root : Codable {
    let items : [Product]
}

struct ProductA : Codable {
    let id, name: String
    let productype: ProductType
    let propertyOfA : String
}

struct ProductB : Codable {
    let id, name: String
    let productype: ProductType
    let propertyOfB : String
}

struct ProductC : Codable {
    let id, name: String
    let productype: ProductType
    let propertyOfC, propertyOfC2 : String
}

enum Product : Codable {
    
    case a(ProductA), b(ProductB), c(ProductC)
    
    enum CodingKeys : String, CodingKey { case productype }
    
    init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        let type = try  container.decode(ProductType.self, forKey: .productype)
        let singleContainer = try decoder.singleValueContainer()
        switch type {
            case .a : self = .a(try singleContainer.decode(ProductA.self))
            case .b : self = .b(try singleContainer.decode(ProductB.self))
            case .c : self = .c(try singleContainer.decode(ProductC.self))
        }
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        switch self {
            case .a(let productA): try container.encode(productA)
            case .b(let productB): try container.encode(productB)
            case .c(let productC): try container.encode(productC)
        }
    }
}

并解码

let jsonString = """
{
    "items": [
        {
            "id": "1",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"

        },
        {
            "id": "2",
            "name": "name",
            "propertyOfA": "12",
            "productype": "a"
        },
        {
            "id": "3",
            "name": "name",
            "propertyOfA": "1243",
            "productype": "a"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfB": "1243",
            "productype": "b"
        },
        {
            "id": "1",
            "name": "name",
            "propertyOfC": "1243",
            "propertyOfC2": "1243",
            "productype": "c"
        }
        ]
}
"""
do {
    let result = try JSONDecoder().decode(Root.self, from: Data(jsonString.utf8))
    print(result)
} catch { print(error)}

读取枚举值,还可以使用switch.


推荐阅读