3
votes

ignorer l'objet nul dans le tableau lors de l'analyse avec Codable swift

J'analyse cette API avec Swift Codable

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

avec ce PaginationModel:

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

et User Model:

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)
    }
}

j'appelle jsonDecoder comme ceci pour analyser l'API json:

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

maintenant mon problème est null dans searchResult Array. il a été analysé correctement et quand j'accède aux données dans paginationModel j'ai trouvé null dans le tableau.

comment puis-je tout ignorer null lors de l'analyse de l'API, et le résultat sera un tableau sans null


2 commentaires

@ user28434 j'ai mis à jour ma question


oui avec ça?, il est normalement analysé à null. je veux un tableau sans objet nul


3 Réponses :


0
votes

Solution simple, filtrez les données après décodage

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


4 commentaires

cela peut être une solution, mais je recherche une meilleure solution. surtout tout ce qui doit être fait dans PaginationModel


Le problème est que le T générique peut être un objet unique aussi bien qu'un tableau. La meilleure solution est de filtrer les valeurs null déjà présentes sur le serveur.


@Sajjad, PaginationModel n'a actuellement pas assez d'informations sur le type réel utilisé dans le générique, vous devrez transmettre les informations que vous recherchez réellement Array , puis dans < code> init (du décodeur: Decoder) décode-le d'abord comme [T?] , puis compactMap en [T] et enregistrer dans la propriété.


D'accord. si je change data: T? en data: [T?] , puis-je le gérer?



0
votes

Vous pouvez ajouter une vérification de type de tableau dans le décodage:

  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 commentaires

3
votes

En premier lieu, je conseillerais de toujours considérer que PaginationModel est composé de tableaux. Il n'est pas nécessaire de passer [User] comme type générique, vous pouvez simplement passer User . Ensuite, l'analyseur peut utiliser la connaissance qu'il analyse les tableaux et gère null automatiquement:

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 }
    }
}

Vous pouvez supprimer les options ici et utiliser des valeurs par défaut à la place: p>

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 }
    }
}


7 commentaires

Je recommanderais également de rendre le total (probable) et les données (certainement) obligatoires plutôt que facultatifs. Cela simplifiera beaucoup cela en se débarrassant de decodeIfPresent. (De même, le nom et la famille sont-ils vraiment facultatifs? Y a-t-il une différence entre une chaîne "manquante" et une chaîne vide?)


@RobNapier Très vrai.


c'est exactement ce que je veux. tnx @Sulthan


@RobNapier je suis d'accord avec vous. mais notre développeur côté serveur a dit que s'ils venaient soudainement avec une valeur nulle (à cause de bogues du serveur ou ...), vous devez le gérer


Même dans ce cas, vous feriez mieux de les rendre obligatoires et de les définir par défaut sur des chaînes vides dans le décodeur afin de ne pas avoir à les traiter comme facultatives partout dans le programme.


quelle est la meilleure pratique pour cela? pouvez-vous écrire du code? S'il vous plaît


@Sajjad Mis à jour.