Недавно я был удивлен, обнаружив следующее:
protocol A: Encodable {}
class B: A {
var x = 3
}
class C: A {
var y = 4
}
let items: [A] = [B()]
let encoded = try JSONEncoder().encode(items)
Сбой в Swift с:
Тип «любой A» не может соответствовать «Кодируемому».
пока это работает:
let items: [B] = [B()]
let encoded = try JSONEncoder().encode(items)
Видимо дело в том, что протоколы не соответствуют сами себе . Обходной путь представлен в этом ответе.
Я понимаю рассуждения и обходные пути в ответах, но это все равно довольно неудобно. Оба эти ответа датированы примерно 2017 годом; есть ли более чистый способ решить эту проблему в Swift 5?
это хороший момент, и я думаю, что быстрый способ этого не сделает. В моем случае соответствующие классы идентифицируют себя с помощью атрибута типа. Это промежуточные выходные данные отладки, это не модель, которая сериализуется и десериализуется в одну и ту же модель, поэтому на самом деле это не проблема.
Тем не менее, то, что что-то является кодируемым, не означает, что оно должно быть декодируемым, и наоборот. Я написал довольно много систем, которые являются однонаправленными в обоих направлениях. Нет ничего плохого в том, чтобы уметь кодировать [any Encodable]. Мы просто сейчас не можем. Сделать это возможным все еще включено в концептуальную дорожную карту (т. е. это не обещано, но желательно). Тем не менее, расшифровать [any Decodable] вряд ли когда-нибудь удастся. Здесь нет общего решения.





encode(_:) — это универсальный метод, который принимает конкретный статический тип, соответствующий Encodable. Общая конструкция метода является причиной того, что вы не можете использовать сам протокол. Компилятору необходимо знать структуру статического типа, чтобы иметь возможность синтезировать метод протокола encode(_:).
Закодируйте массив элементов, соответствующий протоколу Encodeable.
это нормально, но ты пытаешься
Закодируйте массив элементов по протоколу Encodeable.
что невозможно. Это действительно с 2017 года по сегодняшний день.
Спасибо, что нашли время ответить и указали на тонкие языковые различия — мне нравится акцент на том, чтобы быть протоколом кодирования, а не соответствовать ему. Я должен признать, что меня больше всего интересуют обходные пути... значит, обходной путь, описанный в другом билете, по-прежнему остается лучшим, что мы можем сделать?
Пример обходного пути :)
struct AnyEncodable: Encodable {
let item: any Encodable
func encode(to encoder: any Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.item)
}
}
let items: [A] = [B()]
let encoded = try JSONEncoder().encode(items.map { AnyEncodable(item: $0) })
Коротко и понятно - спасибо!
Называть item.encode(to: encoder) не правильно, так как вы нарушите стратегии кодирования; вместо этого вам следует получить singleValueEncodingContainer() и закодировать в него значение. См. forums.swift.org/t/… и связанные темы для получения дополнительной информации.
@ItaiFerber, ты прав. Я исправил свой ответ по вашему предложению. Спасибо за вашу бдительность
Обновление выглядит великолепно! Проголосовал за :)
Скажем так, у вас получится это сделать, но как вы тогда собираетесь его декодировать? У вас будет массив значений json, который может быть чем угодно, так как же вы можете написать для него код декодирования?