Aqui estão as implementações simples de DictionaryEncoder/ DictionaryDecoderque envolvem JSONEncoder, JSONDecodere JSONSerialization, que também lidam com codificação / decodificação de estratégias ...
Muito obrigado!, A alternativa seria usar herança, mas o site de chamada não seria capaz de inferir o tipo como um dicionário, pois haveria 2 funções de tipos de retorno diferentes.
user1046037
17
Eu criei uma biblioteca chamada CodableFirebase e seu propósito inicial era usá-la com o Firebase Database, mas ela realmente faz o que você precisa: cria um dicionário ou qualquer outro tipo como em, JSONDecodermas você não precisa fazer a dupla conversão aqui como você faz em outras respostas. Portanto, seria algo como:
importCodableFirebaselet model =Foo(a:1, b:2)let dict =try!FirebaseEncoder().encode(model)
Isso só funcionaria para estruturas com todas as propriedades do mesmo tipo
Leo Dabus,
1
Acabei de tentar "let dict = try JSONDecoder (). Decode ([String: Int] .self, de: JSONEncoder (). Encode (foo))" e recebi "Esperava decodificar o Dicionário <String, Any>, mas encontrei um matriz em vez disso. " você poderia ajudar, por
Não existe uma maneira de fazer isso. Conforme respondido acima, se você não tiver problemas de desempenho, pode aceitar a JSONEncoder+ JSONSerializationimplementação.
Mas prefiro seguir o caminho da biblioteca padrão para fornecer um objeto codificador / decodificador.
classDictionaryEncoder{privatelet jsonEncoder =JSONEncoder()/// Encodes given Encodable value into an array or dictionary
func encode<T>(_ value: T)throws->Anywhere T:Encodable{let jsonData =try jsonEncoder.encode(value)returntryJSONSerialization.jsonObject(with: jsonData, options:.allowFragments)}}classDictionaryDecoder{privatelet jsonDecoder =JSONDecoder()/// Decodes given Decodable type from given array or dictionary
func decode<T>(_ type: T.Type, from json:Any)throws-> T where T:Decodable{let jsonData =tryJSONSerialization.data(withJSONObject: json, options:[])returntry jsonDecoder.decode(type, from: jsonData)}}
Eu definitivamente acho que há algum valor em apenas ser capaz de usar Codablepara codificar de / para dicionários, sem a intenção de chegar a JSON / Plists / qualquer coisa. Existem muitas APIs que apenas devolvem um dicionário, ou esperam um dicionário, e é bom poder intercambiá-las facilmente com estruturas ou objetos Swift, sem ter que escrever um infinito código clichê.
Tenho brincado com alguns códigos baseados na fonte Foundation JSONEncoder.swift (que na verdade implementa codificação / decodificação de dicionário internamente, mas não os exporta).
Ainda é bastante difícil, mas eu o expandi um pouco para que, por exemplo, ele possa preencher os valores ausentes com os padrões durante a decodificação.
Modifiquei o PropertyListEncoder do projeto Swift em um DictionaryEncoder, simplesmente removendo a serialização final do dicionário para o formato binário. Você pode fazer o mesmo sozinho ou pode pegar meu código daqui
Eu escrevi um rápido essência de lidar com isso (não utilizando o protocolo codificável). Tenha cuidado, ele não verifica nenhum tipo de valor e não funciona recursivamente em valores que são codificáveis.
classDictionaryEncoder{var result:[String:Any]init(){
result =[:]}func encode(_ encodable:DictionaryEncodable)->[String:Any]{
encodable.encode(self)return result
}func encode<T, K>(_ value: T, key: K)where K:RawRepresentable, K.RawValue==String{
result[key.rawValue]= value
}}protocolDictionaryEncodable{func encode(_ encoder:DictionaryEncoder)}
Não há uma maneira direta de fazer isso no Codable. Você precisa implementar o protocolo Encodable / Decodable para sua estrutura. Para seu exemplo, pode ser necessário escrever como abaixo
Pensando bem, a questão não tem uma resposta no caso geral, já que a Encodableinstância pode ser algo não serializável em um dicionário, como uma matriz:
let payload =[1,2,3]let encoded =tryJSONEncoder().encode(payload)//"[1,2,3]"
Respostas:
Se você não se importa com a mudança de dados, pode usar algo assim:
Ou uma variante opcional
Supondo que
Foo
esteja em conformidade comCodable
ou realmenteEncodable
, você pode fazer isso.Se você quiser ir por outro caminho (
init(any)
), dê uma olhada neste Init um objeto em conformidade com Codable com um dicionário / arrayfonte
Aqui estão as implementações simples de
DictionaryEncoder
/DictionaryDecoder
que envolvemJSONEncoder
,JSONDecoder
eJSONSerialization
, que também lidam com codificação / decodificação de estratégias ...O uso é semelhante a
JSONEncoder
/JSONDecoder
...e
Por conveniência, coloquei tudo isso em um repositório… https://github.com/ashleymills/SwiftDictionaryCoding
fonte
Eu criei uma biblioteca chamada CodableFirebase e seu propósito inicial era usá-la com o Firebase Database, mas ela realmente faz o que você precisa: cria um dicionário ou qualquer outro tipo como em,
JSONDecoder
mas você não precisa fazer a dupla conversão aqui como você faz em outras respostas. Portanto, seria algo como:fonte
Não tenho certeza se é a melhor maneira, mas você definitivamente pode fazer algo como:
fonte
let dict = try JSONSerialization.jsonObject(with: try JSONEncoder().encode(struct), options: []) as? [String: Any]
fonte
Não existe uma maneira de fazer isso. Conforme respondido acima, se você não tiver problemas de desempenho, pode aceitar a
JSONEncoder
+JSONSerialization
implementação.Mas prefiro seguir o caminho da biblioteca padrão para fornecer um objeto codificador / decodificador.
Você pode tentar com o seguinte código:
Estou tentando forçar aqui a encurtar o exemplo. No código de produção, você deve lidar com os erros de forma adequada.
fonte
Em alguns projetos, usei a reflexão rápida. Mas tome cuidado, objetos codificáveis aninhados, não são mapeados também lá.
fonte
Eu definitivamente acho que há algum valor em apenas ser capaz de usar
Codable
para codificar de / para dicionários, sem a intenção de chegar a JSON / Plists / qualquer coisa. Existem muitas APIs que apenas devolvem um dicionário, ou esperam um dicionário, e é bom poder intercambiá-las facilmente com estruturas ou objetos Swift, sem ter que escrever um infinito código clichê.Tenho brincado com alguns códigos baseados na fonte Foundation JSONEncoder.swift (que na verdade implementa codificação / decodificação de dicionário internamente, mas não os exporta).
O código pode ser encontrado aqui: https://github.com/elegantchaos/DictionaryCoding
Ainda é bastante difícil, mas eu o expandi um pouco para que, por exemplo, ele possa preencher os valores ausentes com os padrões durante a decodificação.
fonte
Modifiquei o PropertyListEncoder do projeto Swift em um DictionaryEncoder, simplesmente removendo a serialização final do dicionário para o formato binário. Você pode fazer o mesmo sozinho ou pode pegar meu código daqui
Ele pode ser usado assim:
fonte
Eu escrevi um rápido essência de lidar com isso (não utilizando o protocolo codificável). Tenha cuidado, ele não verifica nenhum tipo de valor e não funciona recursivamente em valores que são codificáveis.
fonte
Não há uma maneira direta de fazer isso no Codable. Você precisa implementar o protocolo Encodable / Decodable para sua estrutura. Para seu exemplo, pode ser necessário escrever como abaixo
fonte
Eu fiz um pod aqui https://github.com/levantAJ/AnyCodable para facilitar a decodificação e codificação
[String: Any]
e[Any]
E você é capaz de decodificar e codificar
[String: Any]
e[Any]
fonte
Se você estiver usando o SwiftyJSON , poderá fazer algo assim:
JSON(data: JSONEncoder().encode(foo)).dictionaryObject
fonte
Aqui está uma solução baseada em protocolo:
E aqui está como usá-lo:
fonte
Aqui está o dicionário -> objeto. Swift 5.
fonte
Pensando bem, a questão não tem uma resposta no caso geral, já que a
Encodable
instância pode ser algo não serializável em um dicionário, como uma matriz:Além disso, escrevi algo semelhante como estrutura .
fonte