Swift Codable: impossibile decodificare il dizionario di tipo [String: Any] o [String: Decodable]

-3
Vote

Nel mio inizializzatore personalizzato vorrei decodificare un dizionario da JSON e quindi assegnare i suoi valori alle proprietà nella classe. Con mia grande sorpresa, il compilatore non mi permette di decodificare il dizionario, ottengo l'errore:

Nel mio inizializzatore personalizzato vorrei decodificare un dizionario da JSON e quindi assegnare i suoi valori alle proprietà nella classe. Con mia grande sorpresa, il compilatore non mi permette di decodificare il dizionario, ottengo l'errore:
Value of protocol type 'Any' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols
Value of protocol type 'Any' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols Il valore del tipo di protocollo"Any"non può essere conforme a"Decodable"; solo i tipi struct/enum/class possono essere conformi ai protocolli

Se provo a decodificare un dizionario di tipo[String: Decodable] il messaggio di errore dice:

Se provo a decodificare un dizionario di tipo[String: Decodable] il messaggio di errore dice:
Value of protocol type 'Decodable' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols
Value of protocol type 'Decodable' cannot conform to 'Decodable'; only struct/enum/class types can conform to protocols Il valore del tipo di protocollo"Decodificabile"non può essere conforme a"Decodificabile"; solo i tipi struct/enum/class possono essere conformi ai protocolli

Il mio inizializzatore ha questo aspetto:

Il mio inizializzatore ha questo aspetto:
public let total: Int

required init(from decoder: Decoder) throws {
    let container=try decoder.container(keyedBy: CodingKeys.self)
    ...
    if let dict=try container.decodeIfPresent([String: Any].self, forKey: .tracks),
           let value=dict["total"] as? Int { // Error is displayed at this line
        total=value
    } else {
        total=0
    }
    ...
}
public let total: Int required init(from decoder: Decoder) throws { let container=try decoder.container(keyedBy: CodingKeys.self) ... if let dict=try container.decodeIfPresent([String: Any].self, forKey: .tracks), let value=dict["total"] as? Int { // Error is displayed at this line total=value } else { total=0 } ... } totale affittato pubblico: Int required init (from decoder: Decoder) lancia { let container=try decoder.container (keyedBy: CodingKeys.self) ... se let dict=try container.decodeIfPresent ([String: Any] .self, forKey: .tracks), lascia value=dict["total"] as? Int {// Error viene visualizzato su questa riga totale=valore } altro { totale=0 } ... }

Quando ho cercato la risposta ho trovato this answer e in base ad essa il codice sopra non dovrebbe causare problemi.

Quando ho cercato la risposta ho trovato this answer questa risposta e secondo essa il codice sopra non dovrebbe causare alcun problema.

Source

swift ios codable

-Larme

1 -Larme

1 Answer
1
Vote

Quello che stai cercando è nestedContainer. Ti aiuta a"saltare"un livello gerarchico nel tuo JSON. Vale a dire: immaginiamo che nel tuo codice sia tutto sullo stesso livello (una struttura), ma in JSON non lo è, c'è un dizionario secondario.

A scopo di test, il tuo JSON potrebbe essere simile a:

{
   "title":"normal",
   "tracks": {
                  "name":"myName",
                  "total": 3
              }
}

Se vogliamo nel nostro modello:

struct TestStruct: Codable {
    let title: String
    let name: String
    let total: Int
}

Dobbiamo usare nestedContainer(keyedBy: forKey:):

extension TestStruct {
    enum TopLevelKeys: String, CodingKey {
        case title
        case tracks
    }
    
    enum NestedLevelCodingKeys: String, CodingKey {
        case name
        case total
    }
    
    init(from decoder: Decoder) throws {
        let container=try decoder.container(keyedBy: TopLevelKeys.self)
        self.title=try container.decode(String.self, forKey: .title)
        let subcontainer=try container.nestedContainer(keyedBy: NestedLevelCodingKeys.self, forKey: TopLevelKeys.tracks)
        self.name=try subcontainer.decode(String.self, forKey: .name)
        self.total=try subcontainer.decode(Int.self, forKey: .total) //You can use here a `decodeIfPresent()` if needed, use default values, etc.

    }
}

Nel tuo campione, hai utilizzato decodeIfPresent() per il sottodizionario. Non è chiaro se fosse a scopo di test o se il dizionario secondario a volte non fosse presente. Se è così e puoi avere un JSON come questo:

{
   "title":"normal"
} 

Quindi, prima di chiamare nestedContainer(keyedBy: forKey:), utilizzare if container.contains(TopLevelKeys.tracks) {} e impostare i valori predefiniti, se necessario, in else caso.

Source

Potreste essere interessati

© 2021   Risposta.org