我想将JSON解码为具有Codable协议的对象。
我想要实现的结果是:
[
[ Collection
< collectionType = item
< collectionName = some name`
< data = [ Item
< itemTitle = title
< itemSubtitle = subtitle,
Item
< itemTitle = title
< itemSubtitle = subtitle ],
[ Collection
< collectionType = location
< collectionName = some name`
< data = [ Location
< locationName = someName,
Location
< locationName = someName ],
[ Collection
< collectionType = item
< collectionName = some name`
< data = [ Item
< itemTitle = title
< itemSubtitle = subtitle,
Item
< itemTitle = title
< itemSubtitle = subtitle ],
[ Collection
< collectionType = location
< collectionName = some name`
< data = [ Location
< locationName = someName,
Location
< locationName = someName ]]
JSON如下:
[{
"collectionType": "item",
"collectionName": "some name",
"data": [
{
"itemTitle": "title",
"itemSubtitle": "subtitle",
},
{
"itemTitle": "title",
"itemSubtitle": "subtitle",
}
]
},
{
"collectionType": "location",
"collectionName": "some name",
"data": [
{
"locationName": "a name",
},
{
"locationName": "a name",
}
]
},
{
"collectionType": "item",
"collectionName": "some name",
"data": [
{
"itemTitle": "title",
"itemSubtitle": "subtitle",
},
{
"itemTitle": "title",
"itemSubtitle": "subtitle",
}
]
},
{
"collectionType": "location",
"collectionName": "some name",
"data": [
{
"locationName": "a name",
},
{
"locationName": "a name",
}
]
}
]
如您所见,集合将是项目或位置类型。数据将根据该类型。我应该如何使用 Codable 实现这一目标?
我的对象如下:
class Collection: NSObject, Codable {
// MARK: - Properties
let collectionType: String
let collectionName: String
let data????
// MARK: - Keyes
private enum CodingKeys: String, CodingKey {
case collectionType
case collectionName
}
}
class Item: NSObject, Codable {
// MARK: - Properties
let itemTitle: String
let itemSubtitle: String
// MARK: - Keyes
private enum CodingKeys: String, CodingKey {
case itemTitle
case itemSubtitle
}
}
class Location: NSObject, Codable {
// MARK: - Properties
let locationName: String
// MARK: - Keyes
private enum CodingKeys: String, CodingKey {
case locationName
}
}
如何使用适当的对象传播数据?
我建议两种方法:
方法1
更改数据结构以消除data
描述项目还是位置的歧义:
[{
"collectionName": "some name",
"items": [
{
"itemTitle": "title",
"itemSubtitle": "subtitle",
},
{
"itemTitle": "title",
"itemSubtitle": "subtitle",
}
]
},
{
"collectionName": "some name",
"locations": [
{
"locationName": "a name",
},
{
"locationName": "another name",
}
]
}]
。并修改您的Collection
以具有可选的locations
和可选的items
。
方法2
如果更改 JSON 结构不是一种选择,那么我建议将Collection
类更改为:
class Collection: Codable {
let collectionType: String
let collectionName: String
let data: [CollectionData]
}
。并创建一个枚举CollectionData
:
enum CollectionError: Error {
case invalidData
}
enum CollectionData {
case item(Item)
case location(Location)
}
extension CollectionData: Codable {
init(from decoder: Decoder) throws {
if let item = try? Item(from: decoder) {
self = .item(item)
return
}
if let location = try? Location(from: decoder) {
self = .location(location)
return
}
throw CollectionError.invalidData
}
func encode(to encoder: Encoder) throws {
switch self {
case .item(let item):
try item.encode(to: encoder)
case .location(let location):
try location.encode(to: encoder)
}
}
}
两种方法的优缺点:
方法1
优点:使数据更具自我描述性
缺点:允许既不items
也不locations
的集合
方法2
优点:适用于现有数据结构
缺点:将允许部分Location
和部分Item
的data
阵列
除非你的真实代码有更多内容,否则你似乎完全按照默认值定义CodingKeys
,所以你可能会删除它。
与其使用条件解析,我建议您可以使用具有可选值的多个属性的公共类,并根据要求使用它。 请参考下面的代码。
例如,如果itemTitle
nil
则执行locationName
逻辑,依此类推。
class Collection: NSObject, Codable {
let collectionType: String
let collectionName: String
let data:data?
private enum CodingKeys: String, CodingKey {
case collectionType
case collectionName
}
}
class data: NSObject, Codable {
let itemTitle: String?
let itemSubtitle: String?
let locationName: String?
private enum CodingKeys: String, CodingKey {
case itemTitle
case itemSubtitle
case locationName
}
}