我在当前的项目中非常高兴地使用了Codables-一切都很好,我从盒子里得到的大部分东西都是内置的-完美的!不过,最近我无意中发现了第一个真正的问题,这个问题无法按照我想要的方式自动解决。
问题描述
我有一个来自后端的json,这是一个嵌套的东西。看起来像这样

{
    "id": "fef08c8d-0b16-11e8-9e00-069b808d0ecc",
    "title": "Challenge_Chapter",
    "topics": [
        {
            "id": "5145ea2c-0b17-11e8-9e00-069b808d0ecc",
            "title": "Automation_Topic",
            "elements": [
                {
                    "id": "518dfb8c-0b18-11e8-9e00-069b808d0ecc",
                    "title": "Automated Line examle",
                    "type": "text_image",
                    "video": null,
                    "challenge": null,
                    "text_image": {
                        "background_url": "",
                        "element_render": ""
                    }
                },
                {
                    "id": "002a1776-0b18-11e8-9e00-069b808d0ecc",
                    "title": "Industry 3.0 vs. 4.0: A vision of the new manufacturing world",
                    "type": "video",
                    "video": {
                        "url": "https://www.youtube.com/watch?v=xxx",
                        "provider": "youtube"
                    },
                    "challenge": null,
                    "text_image": null
                },
                {
                    "id": "272fc2b4-0b18-11e8-9e00-069b808d0ecc",
                    "title": "Classmarker_element",
                    "type": "challenge",
                    "video": null,
                    "challenge": {
                        "url": "https://www.classmarker.com/online-test/start/",
                        "description": null,
                        "provider": "class_marker"
                    },
                    "text_image": null
                }
            ]
        }
    ]
}

chapter是根对象,它包含一个主题列表,每个主题包含一个元素列表。很直截了当,但我被最底层的元素所束缚。每个元素都有一个来自后端的枚举,如下所示:
[ video, challenge, text_image ],但iOS应用程序不支持挑战,因此swift中的elementtype enum如下所示:
public enum ElementType: String, Codable {
    case textImage = "text_image"
    case video = "video"
}

当然,它throws,因为首先发生的事情是它试图解码这个枚举的challenge值,但它不在那里,所以我的整个解码失败。
我想要什么
我只希望解码过程忽略无法解码的Elements。我不需要任何Optionals。我只希望它们不出现在topic的元素数组中。
我的推理和它的缺点
当然,我已经做了一些尝试来解决这个问题。第一个,最简单的一个是将ElementType标记为Optional,但是在以后的这个方法中,我将不得不打开所有东西并处理这个-这是一个相当乏味的任务。我的第二个想法是在我的枚举中使用类似于.unsupported的case,但是,再一次,稍后我想使用它生成单元格,我将不得不throw或返回Optional-基本上,与前面的想法相同。
我的最后一个想法,但我还没有测试过,就是编写一个自定义的init()来进行解码,并在那里处理它,但是我不确定是Element还是Topic哪个应该对此负责?如果我把它写进Element中,我什么也不能返回,我必须返回throw,但是如果我把它放进Topic中,我必须成功地解码要数组的元素。问题是,如果在某个时刻我将直接获取append,我将无法在没有Elements的情况下完成。
TL;博士
我希望throw不是init(from decoder: Decoder) throws,而是返回throw

最佳答案

我终于在SR-5953中找到了一些关于这个的东西,但我认为这是一个老套的。
无论如何,对于好奇的人允许这种有损解码你需要手动解码一切。您可以在init(from decoder: Decoder)中编写它,但更好的方法是编写名为struct的新助手。实施过程如下:

struct FailableCodableArray<Element: Decodable>: Decodable {
    // https://github.com/phynet/Lossy-array-decode-swift4
    private struct DummyCodable: Codable {}

    private struct FailableDecodable<Base: Decodable>: Decodable {
        let base: Base?

        init(from decoder: Decoder) throws {
            let container = try decoder.singleValueContainer()
            self.base = try? container.decode(Base.self)
        }
    }

    private(set) var elements: [Element]

    init(from decoder: Decoder) throws {
        var container = try decoder.unkeyedContainer()
        var elements = [Element]()

        if let count = container.count {
            elements.reserveCapacity(count)
        }

        while !container.isAtEnd {
            guard let element = try container.decode(FailableDecodable<Element>.self).base else {
                _ = try? container.decode(DummyCodable.self)
                continue
            }

            elements.append(element)
        }

        self.elements = elements
    }
}

而对于那些失败元素的实际解码,您必须编写一个简单的FailableCodableArray实现,如:
init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    elements = try container.decode(FailableCodableArray<Element>.self, forKey: .elements).elements
}

正如我所说,这个解决方案工作良好,但感觉有点老套。这是一个开放的错误,所以你可以投票给它,让斯威夫特团队看到,像这样的内置将是一个很好的补充!

关于json - 忽略不支持的可销毁物品,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/48702444/

10-13 00:27