Я пытаюсь найти лучший способ кодировать/декодировать массив структур, соответствующих быстрому протоколу, используя новый JSONDecoder/Encoder в Swift 4.
Я составил небольшой пример, чтобы проиллюстрировать проблему:
Сначала у нас есть тег протокола и несколько типов, которые соответствуют этому протоколу.
protocol Tag: Codable {
var type: String { get }
var value: String { get }
}
struct AuthorTag: Tag {
let type = "author"
let value: String
}
struct GenreTag: Tag {
let type = "genre"
let value: String
}
Затем у нас есть Тип статьи, который имеет массив тегов.
struct Article: Codable {
let tags: [Tag]
let title: String
}
Наконец, мы кодируем или декодируем статью
let article = Article(tags: [AuthorTag(value: "Author Tag Value"), GenreTag(value:"Genre Tag Value")], title: "Article Title")
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(article)
let jsonString = String(data: jsonData, encoding: .utf8)
И это структура JSON, которая мне нравится иметь.
{
"title": "Article Title",
"tags": [
{
"type": "author",
"value": "Author Tag Value"
},
{
"type": "genre",
"value": "Genre Tag Value"
}
]
}
Проблема в том, что в какой-то момент мне нужно включить свойство типа для декодирования массива, но для декодирования массива я должен знать его тип.
РЕДАКТИРОВАТЬ:
Мне понятно, почему Decodable не может работать из коробки, но, по крайней мере, Encodable должен работать. Следующая измененная структура Article компилируется, но вылетает со следующим сообщением об ошибке.
fatal error: Array<Tag> does not conform to Encodable because Tag does not conform to Encodable.: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-900.0.43/src/swift/stdlib/public/core/Codable.swift, line 3280
struct Article: Encodable {
let tags: [Tag]
let title: String
enum CodingKeys: String, CodingKey {
case tags
case title
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(tags, forKey: .tags)
try container.encode(title, forKey: .title)
}
}
let article = Article(tags: [AuthorTag(value: "Author Tag"), GenreTag(value:"A Genre Tag")], title: "A Title")
let jsonEncoder = JSONEncoder()
let jsonData = try jsonEncoder.encode(article)
let jsonString = String(data: jsonData, encoding: .utf8)
И это соответствующая часть из Codeable.swift
guard Element.self is Encodable.Type else {
preconditionFailure("\(type(of: self)) does not conform to Encodable because \(Element.self) does not conform to Encodable.")
}
Источник: https://github.com/apple/swift/blob/master/stdlib/public/core/Codable.swift