игнорировать нулевой объект в массиве при разборе с Codable swift - PullRequest
2 голосов
/ 15 марта 2019

Я анализирую этот API с быстрой Codable

"total": 7,
"searchResult": [
    null,
    {
        "name": "joe"
        "family": "adam"
    },
    null,
    {
        "name": "martin"
        "family": "lavrix"
    },
    {
        "name": "sarah"
        "family": "mia"
    },
    null,
    {
        "name": "ali"
        "family": "abraham"
    }
]

с этим PaginationModel:

class PaginationModel<T: Codable>: Codable {
    var total: Int?
    var data: T?

    enum CodingKeys: String, CodingKey {
        case total
        case data = "searchResult"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.total = try container.decodeIfPresent(Int.self, forKey: .total)
        self.data = try container.decodeIfPresent(T.self, forKey: .data)
    }
}

и User Модель:

struct User: Codable {
    var name: String?
    var family: String?
}

я звоню jsonDecoder вот так для разбора API json:

let responseObject = try JSONDecoder().decode(PaginationModel<[User?]>.self, from: json)

теперь моя проблема null в searchResult массиве. он был проанализирован правильно, и когда я получил доступ к data в paginationModel, я нашел null в массиве.

как я могу игнорировать все null при разборе API, и результатом будет массив без каких-либо null

Ответы [ 3 ]

2 голосов
/ 15 марта 2019

Во-первых, я бы посоветовал всегда считать PaginationModel составленным из массивов.Вам не нужно передавать [User] в качестве универсального типа, вы можете просто передать User.Затем анализатор может использовать сведения о том, что он анализирует массивы и обрабатывает null автоматически:

class PaginationModel<T: Codable>: Codable {
    var total: Int?
    var data: [T]?

    enum CodingKeys: String, CodingKey {
        case total
        case data = "searchResult"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.total = try container.decodeIfPresent(Int.self, forKey: .total)

        self.data = (try container.decodeIfPresent([T?].self, forKey: .data))?.compactMap { $0 }
    }
}

Вы можете удалить здесь дополнительные параметры и использовать вместо них некоторые значения по умолчанию:

class PaginationModel<T: Codable>: Codable {
    var total: Int = 0
    var data: [T] = []

    enum CodingKeys: String, CodingKey {
        case total
        case data = "searchResult"
    }

    required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: CodingKeys.self)
        self.total = (try container.decodeIfPresent(Int.self, forKey: .total)) ?? 0

        self.data = ((try container.decodeIfPresent([T?].self, forKey: .data)) ?? []).compactMap { $0 }
    }
}
0 голосов
/ 15 марта 2019

Вы можете добавить проверку типа массива при декодировании:

  required init(from decoder: Decoder) throws {
    let container = try decoder.container(keyedBy: CodingKeys.self)
    self.total = try container.decodeIfPresent(Int.self, forKey: .total)
    self.data = try container.decodeIfPresent(T.self, forKey: .data)

    //add the following:
    if let array =  self.data as? Array<Any?> {
        self.data = ( array.compactMap{$0} as? T)
    }
}
0 голосов
/ 15 марта 2019

Простое решение, фильтр data после декодирования

let responseObject = try JSONDecoder().decode(PaginationModel<[User?]>.self, from: data)
responseObject.data = responseObject.data?.filter{$0 != nil}
...