Быстро декодируемая неоднородная матрица JSON - PullRequest
0 голосов
/ 11 октября 2018

Я прошел через пару вопросов и ответов здесь, на 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 Ответ

0 голосов
/ 11 октября 2018

С вашим кодом много проблем.Просмотрите мой список ниже, сделайте шаг за шагом, и вы обнаружите, что это намного проще, чем вы делаете

  1. Используйте структуры, а не классы, где вы можете (особенноесли данные неизменны)
  2. Используйте let вместо var и не задавайте их начальные значения.
  3. Тщательно продумайте, что делает ваши данные действительными / недействительными.Если значение должно быть для допустимых данных, сделайте свойство необязательным (например, если FeedFeature должен иметь id, чтобы быть действительным, объявите let id: String.
  4. Если значение может отсутствовать (и данные все еще будут действительными), сделайте свойство необязательным, но не установите его в значение по умолчанию (например, let subTitle: String?)
  5. Используйте правильный тип для ваших свойств - startDT - это Date, поэтому объявите как let startDT: Date (или Date?, если это необязательно). Вы можете преобразовать отформатированные даты String в Date значения, установив dateDecodingStrategy. Используйте URL вместо String например. let thumbnail: URL?,
  6. Не объявляйте CodingKeys, когда все имена соответствуют свойствам
  7. Если вы настроили свойстваправильно вы можете удалить свои собственные init(from decoder: Decoder) методы.
  8. Если вы удалите логику Удалить дубликаты / недействительные цены из вашей init - вы сможете выполнить все вышеперечисленное без любой код, отличный от объявлений. Вы можете сделать это после инициализации структуры (возможно, используйте вemp объект для декодирования и последующего копирования в другое вместе с дедупликацией)

Вы можете легко проверить это на игровой площадке.Просто начните с…

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)

, а затем постепенно наращивайте свои объекты, добавляя свойства по одному или более за раз.

...