说JSON看起来像这样:
[
{
"name": "Spot",
"breed": "dalmation"
},
{
"color": "green",
"eats": "lettuce"
},
{
"color": "brown",
"eats": "spinach"
},
{
"color": "yellow",
"eats": "cucumbers"
}
]
从API返回的JSON响应中的第一项始终是狗,而之后的所有项都是乌龟。所以第0项是狗,项目1到N-1是海龟。
如何将其解析为我可以阅读的内容,例如:
struct Result: Codable {
let dog: Dog
let turtles: [Turtle]
}
有可能吗?
答案 0 :(得分:9)
您可以为Result
结构实现自定义解码器。
init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
// Assume the first one is a Dog
self.dog = try container.decode(Dog.self)
// Assume the rest are Turtle
var turtles = [Turtle]()
while !container.isAtEnd {
let turtle = try container.decode(Turtle.self)
turtles.append(turtle)
}
self.turtles = turtles
}
通过少量工作,您可以支持Dog
词典在Turtle
词典数组中的任何位置。
由于您声明您的结构是Codable而不仅仅是Decodable,您还应该从Encodable实现自定义encode(to:)
,但这是留给读者的练习。
答案 1 :(得分:1)
所以你的Array
包含两种类型的元素。这是Type1OrType2
问题的一个很好的例子。对于此类情况,您可以考虑将enum
与关联类型一起使用。对于您的情况,您需要Codable
枚举,并自定义init(from:) throws
& func encode(to:) throws
enum DogOrTurtle: Codable {
case dog(Dog)
case turtle(Turtle)
struct Dog: Codable {
let name: String
let breed: String
}
struct Turtle: Codable {
let color: String
let eats: String
}
}
extension DogOrTurtle {
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
// First try to decode as a Dog, if this fails then try another
self = try .dog(container.decode(Dog.self))
} catch {
do {
// Try to decode as a Turtle, if this fails too, you have a type mismatch
self = try .turtle(container.decode(Turtle.self))
} catch {
// throw type mismatch error
throw DecodingError.typeMismatch(DogOrTurtle.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload conflicts with expected type, (Dog or Turtle)") )
}
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .dog(let dog):
try container.encode(dog)
case .turtle(let turtle):
try container.encode(turtle)
}
}
}
使用这种方法,您无需担心数组中Dog
或Turtle
的排序。元素可以按任何顺序和任何数字出现。
用法:(我故意将狗移到第三个索引处)
let jsonData = """
[
{
"color": "green",
"eats": "lettuce"
},
{
"color": "brown",
"eats": "spinach"
},
{
"name": "Spot",
"breed": "dalmation"
},
{
"color": "yellow",
"eats": "cucumbers"
}
]
""".data(using: .utf8)!
do {
let array = try JSONDecoder().decode([DogOrTurtle].self, from: jsonData)
array.forEach { (dogOrTurtle) in
switch dogOrTurtle {
case .dog(let dog):
print(dog)
case .turtle(let turtle):
print(turtle)
}
}
} catch {
print(error)
}