有可能这是一个XY问题,我也愿意接受这些建议!
我试着用“我的世界”保存数据。在id属性中使用它们的类型对实体进行编码(基本上是任何非严格的块)。然后,该文件包含一个大型实体数组,我希望对其进行解码和实例化。
问题是,使用Decodable时,在开始像container.decode(Zombie.self)一样实例化对象之前,我必须知道对象的类型。我不知道如何创建一个函数来读取id并返回正确的实体类型?
我想这说明了我需要比任何解释更好的解释:
//Entity objects don't actually store their ID since re-encoding it is trivial.
protocol Entity : Decodable {var someProperty : Int {get set}}
struct Zombie : Entity {var someProperty : Int}
struct Skeleton : Entity {var someProperty : Int}
//Using JSON instead of SNBT so we can use JSONDecoder
let jsonData = """
[
{
"id":"zombie",
"someProperty":"3"
},
{
"id" : "skeleton",
"someProperty":"3"
}
]
"""
struct EntityList : Decodable {
var list : [any Entity] = []
init(from decoder : Decoder) throws {
var container = try decoder.unkeyedContainer()
//What should we put here ?
}
}
let decoder = JSONDecoder()
let entityList = try decoder.decode(EntityList.self, from: Data(jsonData.utf8))
//entityList should be [Zombie, Skeleton]目前我正在研究工厂模式,也许这是一个有趣的线索?无论如何,谢谢你的帮助!
(请注意,这个问题与解码文件的实际二进制内容无关,老实说,这是相当困难的,但我已经有了一个工作的Encoder / Decoder__。这只是关于解压缩这些内容的问题,因此我在上面的示例中使用了JSON,因为我们有一个通用的Decoder。)
发布于 2022-08-16 22:05:32
老实说,我还没有使用新的any语法来知道这是否有帮助,但是我已经做了很多次您想做的事情,下面是我是如何做到的。
先设置数据
我们首先声明什么是Zombie和Skeleton。它们可以从协议中继承,也可以是单独的结构.
struct Zombie: Decodable {
let someProperty: Int
}
struct Skeleton: Decodable {
let someProperty: Int
let skeletonSpecificProperty: String
}然后,通过使用枚举并将实体嵌入到其中,我们可以将[anyEntityType]数组转换为同构数组。
enum Entity: Decodable {
case zombie(Zombie)
case skeleton(Skeleton)
}根据您的JSON结构解码enum
我们必须为Entity类型提供一个自定义解码器.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKeys.self)
// First get the `id` value from the JSON object
let type = try container.decode(String.self, forKey: .id)
// check the value for each type of entity we can decode
switch type {
// for each value of `id` create the related type
case "zombie":
let zombie = try Zombie(from: decoder)
self = .zombie(zombie)
case "skeleton":
let skeleton = try Skeleton(from: decoder)
self = .skeleton(skeleton)
default:
// throw an error here... unsupported type or something
}
}现在,这应该允许您将实体数组从JSON解码为[Entity]数组。
处理“未知”类型
处理“未知”类型需要额外的步骤。例如,在上面的代码中。如果JSON包含"id": "creeper",这将导致错误,因为它无法处理该错误。最后你会发现你的整个数组都无法解码。
我创建了几个助手函数来帮助.
如果你创建一个像.
struct Minecraft: Decodable {
let entities: [Entity]
enum RootKeys: String, CodingKey {
case entities
}
}这些帮手..。
extension KeyedDecodingContainer {
func decodeAny<T: Decodable>(_ type: T.Type, forKey key: K) throws -> [T] {
var items = try nestedUnkeyedContainer(forKey: key)
var itemsArray: [T] = []
while !items.isAtEnd {
guard let item = try? items.decode(T.self) else {
try items.skip()
continue
}
itemsArray.append(item)
}
return itemsArray
}
}
private struct Empty: Decodable { }
extension UnkeyedDecodingContainer {
mutating func skip() throws {
_ = try decode(Empty.self)
}
}您可以为Minecraft类型创建这样的自定义解码器.
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: RootKeys.self)
self.entities = try container.decodeAny(Entity.self, forKey: .entities)
}https://stackoverflow.com/questions/73380255
复制相似问题