Swift decodable json heterogenous array



我已经在SO上经历了几个问题和答案,即使它们看起来与我的问题相似,它们也不能完全解决我的问题,因为我尝试了一堆,但它不起作用。这是我的json和我尝试过的方法,我不断收到"无法读取数据,因为它的格式不正确"的错误。

{
"status": 1,
"errorMsg": "success",
"data": [
{
"id": null,
"subMenuId": null,
"type": "Coming Feat",
"data": {
"link": "/google.com",
"title": "Google",
"shortDescription": "This is fun",
"imageUrl": "",
"openInNewWindow": false
},
"datas": null,
"component": null
},
{
"id": "wdub208t2ghf0b",
"subMenuId": "39g3hvb83hb98hv",
"type": "GoingEvent",
"data": {
"eventId": "983gv83hv8hv38",
"sessionId": null,
"title": "Fest",
"iconMarker": "http://google.com/sites.png",
"isPaid": false,
"startDT": "2018-07-18T16:00:00Z",
"endDT": "2018-10-31T22:00:00Z",
"subTitle": null,
"startDate": "Oct, 2018",
"endDate": "Oct, 2018",
"openTime": "04:00 PM",
"closeTime": "10:00 PM",
"thumbnail": "https://static.visit.com/estival23.jpg",
"verticalFeaturedImageUrl": "",
"horizontalImageUrl": "",
"categoryTitle": "Celebration",
"eventCategories": [
"394bf3w9fbv93v8",
"dhvbwuehv80"
],
"locations": [
{
"uniqueName": "fest",
"title": "Got if",
"area": "",
"region": "Put it",
"latitude": 67.14517,
"longitude": 78.797733,
"distance": "N/A",
"startDate": "2018-07-18T16:00:00",
"endDate": "2018-07-27T22:00:00",
"distancevalue": 0,
"duration": "N/A",
"durationValue": 0,
"valid": true,
"hasSet": false
}
],
"prices": null
},
"datas": null,
"component": null
}
]
}
class FeatureData: Decodable {
var link: String?
var title: String?
var shortDescription: String?
var imageUrl: String?
enum CodingKeys: String, CodingKey {
case link
case title
case shortDescription
case imageUrl
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
link = try container.decode(String.self, forKey: .link)
title = try container.decode(String.self, forKey: .title)
shortDescription = try container.decode(String.self, forKey: 
.shortDescription)
imageUrl = try container.decode(String.self, forKey: .imageUrl)
}
init() {
}
}

class FeedFeature: Decodable {
var id: String?
var subMenuId: String?
var type: String?
var data = HomeFeedFeatureData()
enum Codingkeys: String, CodingKey {
case id
case subMenuId
case type
case data
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Codingkeys.self)
id = try container.decode(String.self, forKey: .id)
subMenuId = try container.decode(String.self, forKey: .subMenuId)
type = try container.decode(String.self, forKey: .type)
data = try container.decode(HomeFeedFeatureData.self, forKey: 
.data)
}
init() {
}
}

class EventCalendar: Decodable {
// MARK: Properties
var eventId: String = ""
var sessionId: String = ""
var title: String = ""
var iconMarker: String?
var isPaid: Bool = false
var startDT: String = ""
var endDT: String = ""
var subTitle: String = ""
var startDate: String = ""
var endDate: String = ""
var openTime: String = ""
var closeTime: String = ""
var thumbnail: String = ""
var locations: [EventLocation] = []
var prices: [Price]?
var categoryTitle: String = ""
var isLoadingCell: Bool = false
var isSelected: Bool = false
enum CodingKeys: String, CodingKey {
case eventId = "eventId"
case sessionId = "sessionId"
case title = "title"
case iconMarker = "iconMarker"
case isPaid = "isPaid"
case startDT = "startDT"
case endDT = "endDT"
case subTitle = "subTitle"
case startDate = "startDate"
case endDate = "endDate"
case openTime = "openTime"
case closeTime = "closeTime"
case thumbnail = "thumbnail"
case locations = "locations"
case prices = "prices"
case categoryTitle = "categoryTitle"
}
init() {}
// MARK: Methods
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
eventId = try container.decode(String.self, forKey: .eventId)
sessionId = try container.decodeIfPresent(String.self, forKey: 
.sessionId) ?? ""
title = try container.decodeIfPresent(String.self, forKey: .title) 
?? ""
iconMarker = try container.decodeIfPresent(String.self, forKey: 
.iconMarker) ?? ""
isPaid = try container.decodeIfPresent(Bool.self, forKey: .isPaid) 
?? false
startDT = try container.decodeIfPresent(String.self, forKey: 
.startDT) ?? ""
endDT = try container.decodeIfPresent(String.self, forKey: .endDT) 
?? ""
subTitle = try container.decodeIfPresent(String.self, forKey: 
.subTitle) ?? ""
startDate = try container.decodeIfPresent(String.self, forKey: 
.startDate) ?? ""
endDate = try container.decodeIfPresent(String.self, forKey: 
.endDate) ?? ""
openTime = try container.decodeIfPresent(String.self, forKey: 
.openTime) ?? ""
closeTime = try container.decodeIfPresent(String.self, forKey: 
.closeTime) ?? ""
thumbnail = try container.decodeIfPresent(String.self, forKey: 
.thumbnail) ?? ""
locations = try container.decodeIfPresent([EventLocation].self, 
forKey: .locations) ?? []
categoryTitle = try container.decodeIfPresent(String.self, forKey: 
.categoryTitle) ?? ""
// Remove duplicate/invaid prices - The same logic as 
EventMapComponent
if let tempPrice = try container.decode([Price]?.self, forKey: 
.prices) {
var uniquePrices: [Price] = []
for price in tempPrice {
if !uniquePrices.contains(where: { (checkPrice) -> Bool in
checkPrice.priceInfo == price.priceInfo &&
checkPrice.value == price.value &&
checkPrice.currencyCode == price.currencyCode
}),
price.priceInfo.count > 0 &&
price.value.count > 0 &&
price.currencyCode.count > 0 &&
price.bookingUrl.count > 0 {
// Filter for 0 value prices
if let priceValue = Double(price.value), priceValue > 0 
{
uniquePrices.append(price)
}
}
}
prices = uniquePrices
}
isSelected = BookMarkManager.shared.isFavoriteItem(by: eventId)
}
} 

你的代码有很多问题。浏览下面的列表,一步一步地走,你会发现它比你做的要容易得多。

  1. 尽可能使用结构而不是类(特别是如果数据是不可变的(
  2. 使用let而不是var,并且不要给它们初始值。
  3. 仔细考虑是什么使您的数据有效/无效。如果有效数据必须存在值,请将该属性设置为非可选属性(例如,如果FeedFeature必须具有有效id,则声明let id: String
  4. 如果某个值可能不存在(并且数据仍然有效(,请将该属性设置为可选,但不要将其设置为默认值(例如let subTitle: String?(
  5. 为您的属性使用正确的类型 -startDT是一个Date,因此声明为let startDT: Date(如果它是可选的,则声明为Date?(。您可以通过设置dateDecodingStrategy将格式化的String日期转换为Date值。使用URL而不是String例如。let thumbnail: URL?
  6. 当名称都与属性匹配时,不要声明编码键
  7. 如果已正确设置属性,则可以删除自己的init(from decoder: Decoder)方法。
  8. 如果您从init中删除删除重复/无效价格逻辑 - 您应该能够执行上述所有操作,而无需声明以外的任何代码。您可以在初始化结构后执行此操作(可能使用临时对象进行解码,然后与重复数据消除一起复制到另一个对象中(

您可以在操场上轻松测试这一点。刚从...

let data = """
{your json}
"""".data(using: .utf8)!
struct Response: Decodable {
let status: Int
let errorMsg: String
let data: [FeedFeature]
}
struct FeedFeature: Decodable {
}
JSONDecoder().decode([Response.self], from: data)

然后逐步构建对象,一次添加一个或多个属性。

相关内容

  • 没有找到相关文章

最新更新