首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在不知道对象类型的情况下解码对象

在不知道对象类型的情况下解码对象
EN

Stack Overflow用户
提问于 2022-08-16 20:57:31
回答 1查看 98关注 0票数 1

有可能这是一个XY问题,我也愿意接受这些建议!

我试着用“我的世界”保存数据。在id属性中使用它们的类型对实体进行编码(基本上是任何非严格的块)。然后,该文件包含一个大型实体数组,我希望对其进行解码和实例化。

问题是,使用Decodable时,在开始像container.decode(Zombie.self)一样实例化对象之前,我必须知道对象的类型。我不知道如何创建一个函数来读取id并返回正确的实体类型?

我想这说明了我需要比任何解释更好的解释:

代码语言:javascript
复制
//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。)

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2022-08-16 22:05:32

老实说,我还没有使用新的any语法来知道这是否有帮助,但是我已经做了很多次您想做的事情,下面是我是如何做到的。

先设置数据

我们首先声明什么是ZombieSkeleton。它们可以从协议中继承,也可以是单独的结构.

代码语言:javascript
复制
struct Zombie: Decodable {
  let someProperty: Int
}

struct Skeleton: Decodable {
  let someProperty: Int
  let skeletonSpecificProperty: String
}

然后,通过使用枚举并将实体嵌入到其中,我们可以将[anyEntityType]数组转换为同构数组。

代码语言:javascript
复制
enum Entity: Decodable {
  case zombie(Zombie)
  case skeleton(Skeleton)
}

根据您的JSON结构解码enum

我们必须为Entity类型提供一个自定义解码器.

代码语言:javascript
复制
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",这将导致错误,因为它无法处理该错误。最后你会发现你的整个数组都无法解码。

我创建了几个助手函数来帮助.

如果你创建一个像.

代码语言:javascript
复制
struct Minecraft: Decodable {
  let entities: [Entity]

  enum RootKeys: String, CodingKey {
    case entities
  }
}

这些帮手..。

代码语言:javascript
复制
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类型创建这样的自定义解码器.

代码语言:javascript
复制
init(from decoder: Decoder) throws {
  let container = try decoder.container(keyedBy: RootKeys.self)
  self.entities = try container.decodeAny(Entity.self, forKey: .entities)
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/73380255

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档