Скажем, JSON выглядит так:
[
{
"data": {
"children": [
{
"name": "Ralph"
},
{
"name": "Woofer"
}
]
}
},
{
"data": {
"children": [
{
"name": "Spot"
},
{
"name": "Trevor"
}
]
}
}
]
Где у вас есть эта очень странная структура, где корневой элемент - это массив с двумя объектами, и каждый из этих двух объектов представляет собой массив словарей Dog.
Но проблема в том, что в массиве Dog два ключа! Вы должны пройти через data и children, чтобы добраться до него. Я видел этот ответ изображает это с одной глубиной ключа, но я не могу воспроизвести результат, когда он вложен в две глубины.
Я хочу, чтобы результат был (каким бы странным он ни казался) примерно таким, где оба списка хранятся отдельно:
struct Result: Codable {
let dogs1: [Dog]
let dogs2: [Dog]
}
Я знаю, что мне нужен собственный инициализатор / декодер, но я очень не уверен, как получить к нему доступ.
@ DávidPásztor Извините, что неясно. Я обновлю вопрос
Иногда проще использовать JSONSerialization. Это работало годами, прежде чем появился Codable.
@rmaddy Я знаю, но я пытаюсь понять этот Swift
@matt Что ты имеешь в виду? Это очень похоже на проприетарный JSON от компании, в которой я работаю, которую я не могу изменить. Если у вас другое представление данных в Swift, я все слышу

Итак, короткий ответ: вы не можете, а длинный ответ длиннее.
tl; dr
Один из способов избавиться от этого - начать с промежуточного представления ваших структур. Что-то вроде этого:
struct Intermediate: Codable {
struct Dog: Codable {
let name: String
}
struct Children: Codable {
let children: [Dog]
}
let data: Children
}
а затем вы можете преобразовать это в свою структуру Result. И вы можете преобразовать свою структуру Result в промежуточную для сериализации. Это позволяет избежать более сложного использования ключей и кодировщиков. Вы можете оставить промежуточные представления приватными в своем модуле, если не хотите, чтобы кто-то в них тыкался.
Подождите, так что длинный ответ - что вы можете, правда? Это немного утомительно?
Да, это может быть сделано. Да, это скучно. и да, вероятно, лучше перекодировать ваш json, чем ваш swift, если это имеет смысл.
Хотелось бы, чтобы это действительно очень странный JSON, но я не контролирую его
Используйте промежуточные структуры, чтобы погрузиться в мусорную корзину и собрать нужные данные, а затем избавиться от них.
Итак, начнем со структуры Dog, объявленной на верхнем уровне:
struct Dog : Decodable { let name : String }
В вашем фактическом коде создайте временные локальные структуры, чтобы обернуть его и декодировать JSON:
struct TheChildren : Decodable { let children : [Dog] }
struct TheData : Decodable { let data : TheChildren }
let arr = try! JSONDecoder().decode([TheData].self, from: yourJSONdata)
Теперь просто вытащите желаемых Собак:
let dogs = arr.map {$0.data.children}
/*
[[Dog(name: "Ralph"), Dog(name: "Woofer")],
[Dog(name: "Spot"), Dog(name: "Trevor")]]
*/
Это массив массивов Dogs, поэтому оба «массива обслуживаются отдельно», поскольку они являются отдельными элементами массива результатов. Это кажется вполне разумным представлением.
Теперь, если вы хотите добавить эту информацию в новую структуру, прекрасно. Она не будет такой же, как ваша постулированная структура Result, потому что имена dogs1 и dogs2 нигде не появляются в данных, и вы не можете составить имя свойства во время выполнения (ну, в Swift 4.2 вы вроде как можете, но это другая история). Но дело в том, что у вас есть данные о собаках, легко и без дополнительных материалов. И нет реальной причины, по которой доступ к первому массиву под именем dogs1 лучше, чем получение его по индексу как dogs[0]; действительно, последнее на самом деле лучше. Окончание имени свойства индексным номером - всегда. Плохой запах, предполагающий, что вам действительно нужна какая-то коллекция.
Вы можете декодировать этот JSON без необходимости вводить промежуточные структуры, сохраняя при этом безопасность типов, декодируя внешний Dictionary, единственный ключ которого - data, как вложенный Dictionary типа [String:[String:[Dog]]], что довольно беспорядочно, но работает, поскольку у вас есть только 2 вложенных слоя и отдельные ключи во внешних словарях.
struct Dog: Codable {
let name:String
}
struct Result: Codable {
let dogs1: [Dog]
let dogs2: [Dog]
enum DogJSONErrors: String, Error {
case invalidNumberOfContainers
case noChildrenContainer
}
init(from decoder: Decoder) throws {
var containersArray = try decoder.unkeyedContainer()
guard containersArray.count == 2 else { throw DogJSONErrors.invalidNumberOfContainers}
let dogsContainer1 = try containersArray.decode([String:[String:[Dog]]].self)
let dogsContainer2 = try containersArray.decode([String:[String:[Dog]]].self)
guard let dogs1 = dogsContainer1["data"]?["children"], let dogs2 = dogsContainer2["data"]?["children"] else { throw DogJSONErrors.noChildrenContainer}
self.dogs1 = dogs1
self.dogs2 = dogs2
}
}
Затем вы можете просто декодировать экземпляр Result следующим образом:
do {
let dogResults = try JSONDecoder().decode(Result.self, from: dogsJSONString.data(using: .utf8)!)
print(dogResults.dogs1,dogResults.dogs2)
} catch {
print(error)
}
Кажется, не работает Я получаю keyNotFound(CodingKeys(stringValue: "author", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), _DictionaryCodingKey(stringValue: "data", intValue: nil), _DictionaryCodingKey(stringValue: "children", intValue: nil), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"author\", intValue: nil) (\"author\").", underlyingError: nil)) с распечатанного error.
@DougSmith Я просто скопировал вставленный JSON из вашего вопроса, и он работает нормально. Это сообщение об ошибке особенно странно, поскольку в структурах или JSON не должно быть ключа author. Вы уверены, что точно скопировали этот код и использовали JSON из вопроса?
Итак, у вас, по сути, есть тип
Dog, который имеет свойствоname, и вы хотите декодировать один массив[Dog]из ответа JSON на свой вопрос?