Swift Codable: não é possível decodificar o dicionário do tipo [String: Any] ou [String: Decodable]

-3
Voto

No meu inicializador personalizado, gostaria de decodificar um dicionário de JSON e atribuir seus valores às propriedades da classe. Para minha surpresa, o compilador não me permite decodificar o dicionário, recebo o erro:

No meu inicializador personalizado, gostaria de decodificar um dicionário de JSON e, em seguida, atribuir seus valores às propriedades na classe. Para minha surpresa, o compilador não me permite decodificar o dicionário, recebo o erro:
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 Valor do tipo de protocolo 'Qualquer' não pode estar em conformidade com 'Decodificável'; apenas os tipos struct/enum/class podem estar em conformidade com os protocolos

Se eu tentar decodificar o dicionário do tipo[String: Decodable], a mensagem de erro diz:

Se eu tentar decodificar o dicionário do tipo[String: Decodable], a mensagem de erro diz:
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 O valor do tipo de protocolo 'Decodificável' não pode estar em conformidade com 'Decodificável'; apenas os tipos struct/enum/class podem estar em conformidade com os protocolos

Meu inicializador tem esta aparência:

Meu inicializador tem esta aparência:
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 } ... } total locado público: Int init necessário (do decodificador: decodificador) throws { let container=try decoder.container (keyedBy: CodingKeys.self) ... if let dict=try container.decodeIfPresent ([String: Any] .self, forKey: .tracks), deixe valor=dict["total"] como? Int {// O erro é exibido nesta linha total=valor } outro { total=0 } ... }

Quando procurei a resposta, encontrei this answer e de acordo com ele o código acima não deve causar problemas.

Quando procurei a resposta, encontrei this answer esta resposta e de acordo com ela o código acima não deve causar problemas.

Fonte

swift ios codable

-Larme

1 -Larme

1 Responda
1
Voto

O que você está procurando é o nestedContainer. Ajuda a"pular"um nível de hierarquia em seu JSON. Ou seja: vamos imaginar que no seu código está tudo no mesmo nível (uma estrutura), mas no JSON não está, existe um subdicionário.

Para fins de teste, seu JSON pode ser semelhante a:

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

Se quisermos em nosso modelo:

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

Precisamos usar o 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.

    }
}

Em sua amostra, você usou decodeIfPresent() para o subdicionário. Não está claro se era para fins de teste ou se o subdicionário às vezes não estava presente. Se for esse o caso, você pode ter um JSON como este:

{
   "title":"normal"
} 

Então, antes de ligar para o nestedContainer(keyedBy: forKey:), use o if container.contains(TopLevelKeys.tracks) {} e definir valores padrão se necessário no else caso.

Fonte

Você pode interessar

© 2021   OlaMundo.Org