对于包含无法识别的枚举值的数组,Swift 4 JSON解码器有没有办法不抛出?
我正在尝试使用Swift 4的新JSON解码来解析来自远程服务器的JSON。JSON模式包含枚举值,其中一些值我实际上并不需要,我想忽略它们。此外,我还希望足够健壮,以便在JSON模式更改时,我仍然能够读取尽可能多的数据 问题是,当我试图解析包含枚举的任何内容的数组时,除非每个枚举值都与枚举的文本完全匹配,否则解码器会抛出一个异常,而不是跳过它无法解析的数据 下面是一个简单的例子:对于包含无法识别的枚举值的数组,Swift 4 JSON解码器有没有办法不抛出?,json,decoder,swift4,Json,Decoder,Swift4,我正在尝试使用Swift 4的新JSON解码来解析来自远程服务器的JSON。JSON模式包含枚举值,其中一些值我实际上并不需要,我想忽略它们。此外,我还希望足够健壮,以便在JSON模式更改时,我仍然能够读取尽可能多的数据 问题是,当我试图解析包含枚举的任何内容的数组时,除非每个枚举值都与枚举的文本完全匹配,否则解码器会抛出一个异常,而不是跳过它无法解析的数据 下面是一个简单的例子: enum Size: String, Codable { case large = "large"
enum Size: String, Codable {
case large = "large"
case small = "small"
}
enum Label: String, Codable {
case kitchen = "kitchen"
case bedroom = "bedroom"
case livingRoom = "living room"
}
struct Box: Codable {
var contents: String
var size: Size
var labels: [Label]
}
当我解析完全符合我的大小枚举的数据时,我得到了预期的结果:
let goodJson = """
[
{
"contents": "pillows",
"size": "large",
"labels": [
"bedroom"
]
},
{
"contents": "books",
"size": "small",
"labels": [
"bedroom",
"living room"
]
}
]
""".data(using: .utf8)!
let goodBoxes = try? JSONDecoder().decode([Box?].self, from: goodJson)
// returns [{{contents "pillows", large, [bedroom]}},{{contents "books", small, [bedroom, livingRoom]}}]
但是,如果有不符合枚举的内容,解码器会抛出一个异常,我什么也得不到
let badJson = """
[
{
"contents": "pillows",
"size": "large",
"labels": [
"bedroom"
]
},
{
"contents": "books",
"size": "small",
"labels": [
"bedroom",
"living room",
"office"
]
},
{
"contents": "toys",
"size": "medium",
"labels": [
"bedroom"
]
}
]
""".data(using: .utf8)!
let badBoxes = try? JSONDecoder().decode([Box?].self, from: badJson) // returns nil
理想情况下,在这种情况下,我想取回2件物品,其中尺寸符合“小”或“大”,第二件物品有2个有效标签,“卧室”和“客厅”
若我为Box实现自己的init(from:decoder),我可以自己解码标签并抛出任何不符合我的枚举的标签。然而,如果不实现我自己的解码器并自己解析JSON,我就无法理解如何解码[Box]类型以忽略无效的框,这违背了使用Codable的目的
有什么想法吗?我承认这不是最漂亮的解决方案,但这是我想到的,我想我会与大家分享。我在数组上创建了一个扩展,允许这样做。最大的缺点是必须对JSON数据进行解码,然后再进行编码
extension Array where Element: Codable {
public static func decode(_ json: Data) throws -> [Element] {
let jsonDecoder = JSONDecoder()
var decodedElements: [Element] = []
if let jsonObject = (try? JSONSerialization.jsonObject(with: json, options: [])) as? Array<Any> {
for json in jsonObject {
if let data = try? JSONSerialization.data(withJSONObject: json, options: []), let element = (try? jsonDecoder.decode(Element.self, from: data)) {
decodedElements.append(element)
}
}
}
return decodedElements
}
}
不幸的是,我看不出这将如何解决标签不正确的问题,你必须按照你说的去做,覆盖
init(from:Decoder)
,以确保你的标签是有效的。有点痛苦,但你可以自己编写解码
import Foundation
enum Size: String, Codable {
case large = "large"
case small = "small"
}
enum Label: String, Codable {
case kitchen = "kitchen"
case bedroom = "bedroom"
case livingRoom = "living room"
}
struct Box: Codable {
var contents: String = ""
var size: Size = .small
var labels: [Label] = []
init(from decoder: Decoder) throws {
guard let container = try? decoder.container(keyedBy: CodingKeys.self) else {
return
}
contents = try container.decode(String.self, forKey: .contents)
let rawSize = try container.decode(Size.RawValue.self, forKey: .size)
size = Size(rawValue: rawSize) ?? .small
var labelsContainer = try container.nestedUnkeyedContainer(forKey: .labels)
while !labelsContainer.isAtEnd {
let rawLabel = try labelsContainer.decode(Label.RawValue.self)
if let label = Label(rawValue: rawLabel) {
labels.append(label)
}
}
}
}
extension Box: CustomStringConvertible {
var description: String {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
do {
let data = try encoder.encode(self)
if let jsonString = String(data: data, encoding: .utf8) {
return jsonString
}
} catch {
return ""
}
return ""
}
}
let badJson = """
[
{
"contents": "pillows",
"size": "large",
"labels": [
"bedroom"
]
},
{
"contents": "books",
"size": "small",
"labels": [
"bedroom",
"living room",
"office"
]
},
{
"contents": "toys",
"size": "medium",
"labels": [
"bedroom"
]
}
]
""".data(using: .utf8)!
do {
let badBoxes = try JSONDecoder().decode([Box].self, from: badJson)
print(badBoxes)
} catch {
print(error)
}
输出:
[{
"labels" : [
"bedroom"
],
"size" : "large",
"contents" : "pillows"
}, {
"labels" : [
"bedroom",
"living room"
],
"size" : "small",
"contents" : "books"
}, {
"labels" : [
"bedroom"
],
"size" : "small",
"contents" : "toys"
}]
我有一个类似的例子,我只想忽略无效的数组元素,不想解析器在整个数组上失败。是否真的没有办法跳过无效元素,而不是完全“丢弃”数组并使整个解析失败-/比较
[{
"labels" : [
"bedroom"
],
"size" : "large",
"contents" : "pillows"
}, {
"labels" : [
"bedroom",
"living room"
],
"size" : "small",
"contents" : "books"
}, {
"labels" : [
"bedroom"
],
"size" : "small",
"contents" : "toys"
}]