Com alguma inspiração nessa essência que encontrei, escrevi algumas extensões para UnkeyedDecodingContainer
e KeyedDecodingContainer
. Você pode encontrar um link para minha essência aqui . Usando este código, você agora pode decodificar qualquer um Array<Any>
ou Dictionary<String, Any>
com a sintaxe familiar:
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
ou
let array: [Any] = try container.decode([Any].self, forKey: key)
Editar: há uma ressalva que descobri que é decodificar uma série de dicionários. [[String: Any]]
A sintaxe necessária é a seguinte. Você provavelmente desejará lançar um erro em vez de forçar o lançamento:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
EDIT 2: Se você simplesmente deseja converter um arquivo inteiro em um dicionário, é melhor ficar com a API da JSONSerialization, pois não descobri uma maneira de estender o próprio JSONDecoder para decodificar diretamente um dicionário.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
As extensões
// Inspired by https://gist.github.com/mbuchetics/c9bc6c22033014aa0c550d3b4324411a
struct JSONCodingKeys: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int?
init?(intValue: Int) {
self.init(stringValue: "\(intValue)")
self.intValue = intValue
}
}
extension KeyedDecodingContainer {
func decode(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any> {
let container = try self.nestedContainer(keyedBy: JSONCodingKeys.self, forKey: key)
return try container.decode(type)
}
func decodeIfPresent(_ type: Dictionary<String, Any>.Type, forKey key: K) throws -> Dictionary<String, Any>? {
guard contains(key) else {
return nil
}
guard try decodeNil(forKey: key) == false else {
return nil
}
return try decode(type, forKey: key)
}
func decode(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any> {
var container = try self.nestedUnkeyedContainer(forKey: key)
return try container.decode(type)
}
func decodeIfPresent(_ type: Array<Any>.Type, forKey key: K) throws -> Array<Any>? {
guard contains(key) else {
return nil
}
guard try decodeNil(forKey: key) == false else {
return nil
}
return try decode(type, forKey: key)
}
func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
var dictionary = Dictionary<String, Any>()
for key in allKeys {
if let boolValue = try? decode(Bool.self, forKey: key) {
dictionary[key.stringValue] = boolValue
} else if let stringValue = try? decode(String.self, forKey: key) {
dictionary[key.stringValue] = stringValue
} else if let intValue = try? decode(Int.self, forKey: key) {
dictionary[key.stringValue] = intValue
} else if let doubleValue = try? decode(Double.self, forKey: key) {
dictionary[key.stringValue] = doubleValue
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self, forKey: key) {
dictionary[key.stringValue] = nestedDictionary
} else if let nestedArray = try? decode(Array<Any>.self, forKey: key) {
dictionary[key.stringValue] = nestedArray
}
}
return dictionary
}
}
extension UnkeyedDecodingContainer {
mutating func decode(_ type: Array<Any>.Type) throws -> Array<Any> {
var array: [Any] = []
while isAtEnd == false {
// See if the current value in the JSON array is `null` first and prevent infite recursion with nested arrays.
if try decodeNil() {
continue
} else if let value = try? decode(Bool.self) {
array.append(value)
} else if let value = try? decode(Double.self) {
array.append(value)
} else if let value = try? decode(String.self) {
array.append(value)
} else if let nestedDictionary = try? decode(Dictionary<String, Any>.self) {
array.append(nestedDictionary)
} else if let nestedArray = try? decode(Array<Any>.self) {
array.append(nestedArray)
}
}
return array
}
mutating func decode(_ type: Dictionary<String, Any>.Type) throws -> Dictionary<String, Any> {
let nestedContainer = try self.nestedContainer(keyedBy: JSONCodingKeys.self)
return try nestedContainer.decode(type)
}
}
UnkeyedDecodingContainer
'sdecode(_ type: Array<Any>.Type) throws -> Array<Any>
está verificando um nested matriz. Portanto, se você tiver uma estrutura de dados parecida com a seguinte:[true, 452.0, ["a", "b", "c"] ]
Ela puxaria o["a", "b", "c"]
array aninhado . Odecode
método de umUnkeyedDecodingContainer
"retalho" do elemento do contêiner. Não deve causar recursão infinita.{"array": null}
. Portanto, o seuguard contains(key)
passará, mas travará algumas linhas depois ao tentar decodificar o valor nulo para a chave "array". Portanto, é melhor adicionar mais uma condição para verificar se o valor não é realmente nulo antes de chamardecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
tentar:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Também brinquei com esse problema e finalmente escrevi uma biblioteca simples para trabalhar com tipos “JSON genéricos” . (Onde "genérico" significa "sem nenhuma estrutura conhecida de antemão".) O ponto principal é representar o JSON genérico com um tipo concreto:
Esse tipo pode então implementar
Codable
eEquatable
.fonte
Você pode criar uma estrutura de metadados que confirma o
Decodable
protocolo e usa aJSONDecoder
classe para criar um objeto a partir dos dados usando o método de decodificação abaixofonte
metadata
valor. Pode ser qualquer objeto arbitrário.metadata
pode ser qualquer objeto JSON. Portanto, pode ser um dicionário vazio ou qualquer dicionário. "metadata": {} "metadata": {user_id: "id"} "metadata": {preferência: {shows_value: true, language: "en"}} etc.Eu vim com uma solução ligeiramente diferente.
Vamos supor que temos algo mais do que um simples
[String: Any]
para analisar se Any pode ser um array ou um dicionário aninhado ou um dicionário de arrays.Algo assim:
Bem, esta é a minha solução:
Experimente usando
fonte
Quando encontrei a resposta antiga, testei apenas um caso de objeto JSON simples, mas não um caso vazio, o que causará uma exceção de tempo de execução como @slurmomatic e @zoul encontrados. Desculpe por este problema.
Então, tento outra maneira, tendo um protocolo JSONValue simples, implementar a
AnyJSONValue
estrutura de eliminação de tipo e usar esse tipo em vez deAny
. Aqui está uma implementação.E aqui está como usá-lo ao decodificar
O problema com esse problema é que devemos ligar
value.jsonValue as? Int
. Precisamos esperar atéConditional Conformance
pousar em Swift, isso resolveria esse problema ou pelo menos ajudaria a melhorar.[Resposta Antiga]
Eu posto esta questão no fórum de desenvolvedores da Apple e descobri que é muito fácil.
eu posso fazer
no inicializador.
Foi ruim para mim perder isso em primeiro lugar.
fonte
Any
não está em conformidade,Decodable
então não tenho certeza de como essa é a resposta correta.Se você usar o SwiftyJSON para analisar JSON, poderá atualizar para 4.1.0, que tem
Codable
suporte de protocolo. Basta declararmetadata: JSON
e está tudo pronto.fonte
Você pode dar uma olhada em BeyovaJSON
fonte
A maneira mais fácil e sugerida é criar um modelo separado para cada dicionário ou modelo que está em JSON .
Aqui está o que eu faço
Uso:
** Usei o opcional para estar seguro durante a análise, pode ser alterado conforme necessário.
Leia mais sobre este assunto
fonte
Fiz um pod para facilitar o caminho a decodificação + codificação
[String: Any]
,[Any]
. E isso fornece codificar ou decodificar as propriedades opcionais, aqui https://github.com/levantAJ/AnyCodableComo usá-lo:
fonte
Aqui está uma abordagem mais genérica (não apenas
[String: Any]
, mas[Any]
pode ser decodificada) e encapsulada (uma entidade separada é usada para isso) inspirada na resposta de @loudmouth.O uso será semelhante a:
JsonContainer
é uma entidade auxiliar que usamos para encapsular dados de decodificação JSON em objeto JSON (seja uma matriz ou dicionário) sem estender*DecodingContainer
(portanto, não interfere em casos raros em que um objeto JSON não é referido[String: Any]
).Observe que os tipos numéricos e booleanos são apoiados por
NSNumber
, caso contrário, algo como isto não funcionará:fonte
decodifique usando decodificador e chaves de codificação
fonte
fonte