Вдохновленный этой сутью , я написал несколько расширений для UnkeyedDecodingContainer
и KeyedDecodingContainer
. Вы можете найти ссылку на мою суть здесь . Используя этот код, вы теперь можете декодировать любой Array<Any>
или Dictionary<String, Any>
знакомый синтаксис:
let dictionary: [String: Any] = try container.decode([String: Any].self, forKey: key)
или
let array: [Any] = try container.decode([Any].self, forKey: key)
Изменить: я обнаружил одно предостережение, которое заключается в декодировании массива словарей [[String: Any]]
. Требуемый синтаксис выглядит следующим образом. Скорее всего, вы захотите выдать ошибку вместо принудительного приведения:
let items: [[String: Any]] = try container.decode(Array<Any>.self, forKey: .items) as! [[String: Any]]
РЕДАКТИРОВАТЬ 2: если вы просто хотите преобразовать весь файл в словарь, вам лучше придерживаться api из JSONSerialization, так как я не нашел способа расширить сам JSONDecoder для прямого декодирования словаря.
guard let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
// appropriate error handling
return
}
Расширения
// 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>
проверяет для вложенного массива. Итак, если у вас есть структура данных, которая выглядит следующим образом:[true, 452.0, ["a", "b", "c"] ]
она будет извлекать вложенный["a", "b", "c"]
массив.decode
Метода А. Н.UnkeyedDecodingContainer
«хлопков» от элемента из контейнера. Это не должно вызывать бесконечную рекурсию.{"array": null}
. Таким образом, выguard contains(key)
пройдете, но через несколько строк при попытке декодирования нулевого значения для ключа «массив» произойдет сбой. Поэтому перед вызовом лучше добавить еще одно условие, чтобы проверить, действительно ли значение не равно нулюdecode
.} else if let nestedArray = try? decode(Array<Any>.self, forKey: key)
попытки:} else if var nestedContainer = try? nestedUnkeyedContainer(), let nestedArray = try? nestedContainer.decode(Array<Any>.self) {
Я тоже играл с этой проблемой и, наконец, написал простую библиотеку для работы с «общими типами JSON» . (Где «общий» означает «без заранее известной структуры».) Основной момент - это представление универсального JSON с конкретным типом:
Затем этот тип может реализовать
Codable
иEquatable
.источник
Вы можете создать структуру метаданных, которая подтверждает
Decodable
протокол и использоватьJSONDecoder
класс для создания объекта из данных, используя метод декодирования, как показано ниже.источник
metadata
значения. Это может быть любой произвольный объект.metadata
может быть любой объект JSON. Так что это может быть пустой словарь или любой другой словарь. "метаданные": {} "метаданные": {user_id: "id"} "метаданные": {preference: {shows_value: true, language: "en"}} и т. д.Я пришел с немного другим решением.
Предположим, у нас есть нечто большее, чем просто
[String: Any]
анализируемый объект, если Any может быть массивом, вложенным словарем или словарем массивов.Что-то вроде этого:
Что ж, это мое решение:
Попробуйте использовать
источник
Когда я нашел старый ответ, я протестировал только простой случай объекта JSON, но не пустой, который вызовет исключение времени выполнения, такое как @slurmomatic и @zoul found. Извините за эту проблему.
Поэтому я пробую другой способ, используя простой протокол JSONValue, реализую
AnyJSONValue
структуру стирания типа и использую этот тип вместоAny
. Вот реализация.А вот как его использовать при декодировании
Проблема с этим вопросом в том, что мы должны позвонить
value.jsonValue as? Int
. Нам нужно дождатьсяConditional Conformance
приземления в Swift, чтобы решить эту проблему или, по крайней мере, помочь ей стать лучше.[Старый ответ]
Я размещаю этот вопрос на форуме разработчиков Apple, и оказалось, что это очень просто.
я могу сделать
в инициализаторе.
Во-первых, мне было плохо по этому поводу.
источник
Any
не соответствует,Decodable
поэтому я не уверен, насколько это правильный ответ.Если вы используете SwiftyJSON для анализа JSON, вы можете обновить его до версии 4.1.0, которая поддерживает
Codable
протокол. Просто объявите,metadata: JSON
и все готово.источник
Вы можете взглянуть на BeyovaJSON
источник
Самый простой и рекомендуемый способ - создать отдельную модель для каждого словаря или модели в JSON .
Вот что я делаю
Использование:
** Я использовал опцию optional, чтобы быть в безопасности при парсинге, при необходимости можно изменить.
Подробнее по этой теме
источник
Я сделал стручок , чтобы облегчить способ декодирования + кодирующий
[String: Any]
,[Any]
. И это обеспечивает кодирование или декодирование дополнительных свойств, здесь https://github.com/levantAJ/AnyCodableКак это использовать:
источник
Вот более общий (не только
[String: Any]
, но[Any]
декодируемый) и инкапсулированный подход (для этого используется отдельный объект), вдохновленный ответом @loudmouth.Использование будет выглядеть так:
JsonContainer
- это вспомогательная сущность, которую мы используем для переноса декодирования данных JSON в объект JSON (массив или словарь) без расширения*DecodingContainer
(поэтому он не будет мешать в редких случаях, когда объект JSON не подразумевается[String: Any]
).Обратите внимание, что числовые и логические типы поддерживаются
NSNumber
, иначе что-то вроде этого не сработает:источник
декодировать с помощью декодера и ключей кодирования
источник
источник