Swift и Codable: кодируемая древовидная структура, заканчивающаяся значениями, которые подчиняются пользовательскому протоколу

#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 вообще не можете кодировать.