json – 如何解码类型依赖于标记的值数组?

前端之家收集整理的这篇文章主要介绍了json – 如何解码类型依赖于标记的值数组?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我有一个带有一组值的 JSON
  1. [
  2. { "tag": "Foo",… },{ "tag": "Bar",{ "tag": "Baz",]

我想将此数组解码为一个结构数组,其中特定类型取决于标记

  1. protocol SomeCommonType {}
  2.  
  3. struct Foo: Decodable,SomeCommonType { }
  4. struct Bar: Decodable,SomeCommonType { }
  5. struct Baz: Decodable,SomeCommonType { }
  6.  
  7. let values = try JSONDecoder().decode([SomeCommonType].self,from: …)

我怎么做?目前我有这个有点丑陋的包装:

  1. struct DecodingWrapper: Decodable {
  2.  
  3. let value: SomeCommonType
  4.  
  5. public init(from decoder: Decoder) throws {
  6. let c = try decoder.singleValueContainer()
  7. if let decoded = try? c.decode(Foo.self) {
  8. value = decoded
  9. } else if let decoded = try? c.decode(Bar.self) {
  10. value = decoded
  11. } else if let decoded = try? c.decode(Baz.self) {
  12. value = decoded
  13. } else {
  14. throw
  15. }
  16. }
  17. }

然后:

  1. let wrapped = try JSONDecoder().decode([DecodingWrapper].self,from: …)
  2. let values = wrapped.map { $0.value }

有没有更好的办法?

解决方法

你的数组包含有限,可枚举变种的异构对象;听起来像Swift枚举的完美用例.它不适用于多态性,因为从概念上讲,这些“事物”不一定是同一种.他们恰好被标记了.

以这种方式看待它:你有一系列都有标签的东西,有些属于这种类型,有些属于完全不同的类型,还有其他……有时你甚至都不认识标签. Swift enum是捕捉这一想法的完美载体.

所以你有一堆结构共享一个标签属性,但彼此完全不同:

  1. struct Foo: Decodable {
  2. let tag: String
  3. let fooValue: Int
  4. }
  5.  
  6. struct Bar: Decodable {
  7. let tag: String
  8. let barValue: Int
  9. }
  10.  
  11. struct Baz: Decodable {
  12. let tag: String
  13. let bazValue: Int
  14. }

并且您的数组可以包含上述类型的任何实例,或者包含未知类型的实例.所以你有enum TagggedThing(或更好的名字).

  1. enum TagggedThing {
  2. case foo(Foo)
  3. case bar(Bar)
  4. case baz(Baz)
  5. case unknown
  6. }

你的数组,用Swift术语,是[TagggedThing]类型.所以你将TagggedThing类型与Decodable一致,如下所示:

  1. extension TagggedThing: Decodable {
  2. private enum CodingKeys: String,CodingKey {
  3. case tag
  4. }
  5.  
  6. init(from decoder: Decoder) throws {
  7. let container = try decoder.container(keyedBy: CodingKeys.self)
  8. let tag = try container.decode(String.self,forKey: .tag)
  9.  
  10. let singleValueContainer = try decoder.singleValueContainer()
  11. switch tag {
  12. case "foo":
  13. // if it's not a Foo,throw and blame the server guy
  14. self = .foo(try singleValueContainer.decode(Foo.self))
  15. case "bar":
  16. self = .bar(try singleValueContainer.decode(Bar.self))
  17. case "baz":
  18. self = .baz(try singleValueContainer.decode(Baz.self))
  19. default:
  20. // this tag is unknown,or known but we don't care
  21. self = .unknown
  22. }
  23. }
  24. }

现在您可以解码以下JSON:

  1. let json: Data! = """
  2. [
  3. {"tag": "foo","fooValue": 1},{"tag": "bar","barValue": 2},{"tag": "baz","bazValue": 3}
  4. ]
  5. """.data(using: .utf8)

像这样:

  1. let taggedThings = try? JSONDecoder().decode([TagggedThing].self,from: json)

猜你在找的JavaScript相关文章