【问题标题】:Multiple codable object types out of a single JSON return单个 JSON 返回中的多个可编码对象类型
【发布时间】:2018-01-22 19:33:42
【问题描述】:

我的困境是我在 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

有没有一种方法可以创建一个子类或扩展,可以从 Bool 中删除,由 imageName 是否为 null 定义?

感谢大家的帮助。

【问题讨论】:

    标签: ios json swift swift4 codable


    【解决方案1】:

    您不需要两个不同的对象。只需将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) }
    

    【讨论】:

    • 问题是我确实需要多个对象。在项目中,每个实例都有大量的实例(以前他们使用硬编码的数据源,并且有两个对象。现在两个对象都有一个返回)所以我们需要提取每个对象类型,或者重组大量他们的应用程序。如果无法在此级别将它们分开,这是有可能的。
    • 那你得写一个自定义的初始化器。
    【解决方案2】:

    我实际上喜欢 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 更好或更差,但这是另一种方法。

    【讨论】:

    • 嗨,Rob,感谢您的回复。经过一些研究,我认为我希望的确切功能在技术上是不可能的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-06
    相关资源
    最近更新 更多