#json #swift #parsing #codable
#json #swift #синтаксический анализ #кодируемый
Вопрос:
Я пытаюсь заставить древовидную структуру данных подчиняться кодируемому протоколу. Дерево заканчивается «некоторым объектом», который подчиняется протоколу «Terminal». Терминал расширяет Codable.
Каждый узел дерева представляет собой пару. У него есть ключ и значение. Значение является либо парой, либо терминалом.
Есть две основные проблемы:
1) Я хотел бы, чтобы эта структура кодировалась в JSON таким образом, чтобы
class Pair: Codable {
var key: String?
var value: Codable?
}
let outputSimple = Pair(key: "a test key", value: "a test value")
// encodes to
// {"a test key": "a test value"}
// whereas currently encodes to
// {}
let outputComplex = Pair(key: "a parent", value: Pair(key: "another pair", value: "a test value"))
// encodes to
// {"a parent": {"another pair", "a test value"}}
РЕДАКТИРОВАТЬ: часть 2 может немного запутать проблему. Чтобы прояснить проблему выше, если бы у меня
class Pair: Codable {
var key: String
var value: String
}
let p = Pair(key:"foo", value: "bar")
как я мог заставить его выводить {«foo»:»bar»}, а не {key:foo, value:bar}? Я попытался
override func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
container.encode(contentsOf: [key: value])
}
но получите сообщение об ошибке «Метод экземпляра ‘encode(contentsOf:)’ требует, чтобы ‘(ключ: _, значение: _)’ соответствовал ‘Encodable'»
2) Я пытался выполнить следующее, но, похоже, это не сработало. Я получаю «Пара не соответствует протоколу ‘Decodable'»
protocol TreeNode: Codable {}
struct Pair: TreeNode {
var key: String?
var value: TreeNode?
}
extension String: TreeNode {}
Это я могу обойти, сделав TreeNode классом с парой подклассов. Также вероятно, что это правильное быстрое поведение. Тем не менее, я задавался вопросом, может ли больше пар глаз объяснить проблему. Я предположил, что до тех пор, пока я гарантировал, что все значения имеют либо тип pair, либо что-то еще, что подчиняется Codable, тогда это будет работать.
Комментарии:
1. Взгляните на GenericJSON для создания динамического, типобезопасного JSON с использованием
Codable
. Вы могли бы упростить это, чтобы соответствовать вашим потребностям вTree
/Pair
.2. Спасибо. Это подход, который я использую, но он включает в себя обертывание терминальных узлов в классе. Затем метод encode для этого класса выполняет кодирование в стиле GenericJSON
Ответ №1:
Это не может работать.
value
должен быть конкретный тип, который соответствует Codable
, а не сам протокол или второй протокол, соответствующий Codable
Что вы можете сделать, так это объявить value
как универсальный
class Pair<T: Codable>: Codable {
var key: String?
var value: T?
init(key: String?, value: T?) {
self.key = key
self.value = value
}
}
Он может кодировать как outputSimple
и outputComplex
Комментарии:
1. Я предполагаю, что это ответ на часть 2? Часть 1 все равно будет закодирована в {«ключ»: «foo», «значение»: «bar»} для пары (ключ: «foo», значение: «bar»}?
2. На самом деле обе части имеют одну и ту же проблему: вы не можете использовать
Codable
или другой протокол , соответствующийCodable
типу as. Я бы использовалnil
в качестве терминатора.3. К сожалению, в моем обсуждении выше для простоты была исключена другая структура данных «Массив». Массив может иметь любой кодируемый объект, поэтому я не могу указать T в Array<T>, кроме Array<Any>, и Any не поддается кодированию/
4. Возможно, я что-то перепутал, сделав этот вопрос из двух частей. В части 1, теперь предполагая, что ключ и значение являются строками, мне нужно составить словарь «{<значение ключа>: <значение значения>}» вместо стандартного кодируемого {«ключ»: <значение ключа>, «значение»: <значение значения>}
5.
T
может быть также массивом, например[Pair]
, но все элементы должны иметь один и тот жеvalue
тип. ВыAny
вообще не можете кодировать.