单个JSON返回中的多个可编码对象类型

时间:2018-01-22 19:33:43

标签: ios json swift swift4 codable

我的困境是我在JSON响应中从一个表中接收到两种不同的对象类型。以下是返回中两种类型的响应示例。

"supplementaryItems": [
    {
        "header": "Doodle",
        "subHeader": "It's a drawing.",
        "slideID": 4,
        "imageName": null,
        "textItems": null,
        "sortOrder": 0
    },
    {
        "header": "Cell Phones",
        "subHeader": "No phones please",
        "slideID": 8,
        "imageName": "welcome_icon_cellphones",
        "textItems": ["first","second","third"],
        "sortOrder": 1
    }
]

我们希望做的是在这里创建两种不同类型的对象。一个textOnlyItem和一个imageWithTextItem

是否可以创建一个作为子类或扩展名的方法,可以根据BoolimageName定义的null来定义?

感谢您的帮助。

2 个答案:

答案 0 :(得分:2)

您不需要两个不同的对象。只需声明imageNametextItems为可选项,即可处理null案例。

您只需检查imageName是否为nil

let jsonString = """
{"supplementaryItems": [
{
"header": "Doodle",
"subHeader": "It's a drawing.",
"slideID": 4,
"imageName": null,
"textItems": null,
"sortOrder": 0
},
{
"header": "Cell Phones",
"subHeader": "No phones please",
"slideID": 8,
"imageName": "welcome_icon_cellphones",
"textItems": ["first","second","third"],
"sortOrder": 1
}
]
}
"""
struct Root : Decodable {
    let supplementaryItems : [SupplementaryItem]
}

struct SupplementaryItem : Decodable {
    let header : String
    let subHeader : String
    let slideID : Int
    let imageName : String?
    let textItems : [String]?
    let sortOrder : Int
}

do {
    let data = Data(jsonString.utf8)
    let result = try JSONDecoder().decode(Root.self, from: data)
    for item in result.supplementaryItems {
       if let imageName = item.imageName {
           print(imageName + " has text items")
       } else {
           print(item.header + " has no text items")
       }
    }
} catch { print(error) }

答案 1 :(得分:0)

我实际上喜欢vadian的一种方法。但我认为在你的情况下需要进行大量的重构。

另一种方法是只使用JSONSerialization并手动构建异构数组。 JSONSerialization不被弃用,它不会像JSONDecoder那样自动执行。

另一种方法是使用JSONDecoder,编写尝试将其解码为ImageItem的自定义初始值设定项,如果失败,请尝试将其解码为TextItem

protocol SupplementaryItem {
    var header:    String    { get }
    var subHeader: String    { get }
    var slideID:   Int       { get }
    var sortOrder: Int       { get }
    var textItems: [String]? { get }
}

struct TextItem: SupplementaryItem, Codable {
    let header:    String
    let subHeader: String
    let slideID:   Int
    let sortOrder: Int
    let textItems: [String]?
}

struct ImageItem: SupplementaryItem, Codable {
    let header:    String
    let subHeader: String
    let slideID:   Int
    let sortOrder: Int
    let textItems: [String]?
    let imageName: String
}

struct ResponseObject: Decodable {
    let supplementaryItems: [SupplementaryItem]

    enum CodingKeys: String, CodingKey {
        case supplementaryItems
    }

    init(from decoder: Decoder) throws {
        let values = try decoder.container(keyedBy: CodingKeys.self)
        var container = try values.nestedUnkeyedContainer(forKey: .supplementaryItems)
        if container.count == nil {
            throw DecodingError.dataCorruptedError(in: container, debugDescription: "Expected array for supplementaryItems")
        }
        var items = [SupplementaryItem]()
        while !container.isAtEnd {
            if let item = try? container.decodeIfPresent(ImageItem.self), let imageItem = item {
                items.append(imageItem)
            } else {
                let textItem = try container.decode(TextItem.self)
                items.append(textItem)
            }
        }
        supplementaryItems = items
    }
}

然后:

let string = """
    {
        "supplementaryItems": [
            {
                "header": "Doodle",
                "subHeader": "It's a drawing.",
                "slideID": 4,
                "imageName": "foo",
                "textItems": null,
                "sortOrder": 0
            },
            {
                "header": "Cell Phones",
                "subHeader": "No phones please",
                "slideID": 8,
                "imageName": "welcome_icon_cellphones",
                "textItems": ["first","second","third"],
                "sortOrder": 1
            }
        ]
    }
    """

let data = string.data(using: .utf8)!

let json = try! JSONDecoder().decode(ResponseObject.self, from: data)
print(json)

我不相信这比使用JSONSerialization更好或更糟,但它是另一种方法。