【问题标题】:How do I convert this JSON into a Swift structure using the Decodable protocol?如何使用 Decodable 协议将此 JSON 转换为 Swift 结构?
【发布时间】:2017-11-19 12:55:49
【问题描述】:

注意:我已经看过这个问题 -> How do I use custom keys with Swift 4's Decodable protocol? 但它没有解释如何编码/解码枚举

这是我想要的结构:

struct MyStruct: Decodable {
    let count: PostType
}

enum PostType: Decodable {
    case fast(value: Int, value2: Int)
    case slow(string: String, string2: String)
}

现在我知道我希望我的结构看起来如何,问题是:

  1. 我不知道init 函数在PostType 枚举中应该是什么样子。

我使用下面的代码来帮助我快速构建 JSON。

    let jsonData = """
    {
       "count": {
          "fast" :
           {
               "value": 4,
               "value2": 5
           }
       }
    }
    """.data(using: .utf8)!

    // Decoding
    do {
        let decoder = JSONDecoder()
        let response = try decoder.decode(MyStruct.self, from: jsonData)
        print(response)
    } catch {
        print(error)
    }

谁能帮我解决这个问题?

Edit1我的 JSON 看起来像这样

   {
        "count": {
           "fast" :
            {
                "value": 4,
                "value2": 5
            }
        }
    }

init 在PostType enum 中应该是什么样子?

【问题讨论】:

  • 这不是重复的。这个问题是解码一个结构。这是用元组解码枚举。我知道如何解码结构,但使用枚举就大不一样了。
  • 您期望的关联枚举值的 JSON 表示形式是什么?
  • @vadian 就是这样。我知道我希望我的结构看起来像 MyStruct,但我不知道 JSON 应该是什么样子。如果有人能告诉我,那么我可以让 JSON 成为那个值。
  • JSON 有一组有限的类型:Array、Dictionary 作为集合类型,String、Number、Bool 和 null 作为值类型。您可以将枚举表示为 String / Int 的数组,或者将每个值表示为键 / 值对或以大小写为键的字典。
  • @vadian 我已经编辑了我的问题并包含了我的 JSON 的样子。我如何让 PostType 枚举来解析这个?

标签: json swift codable


【解决方案1】:

由于带有关联类型的 enum 与任何 JSON 类型都不匹配,因此您需要更多的手工操作并编写自定义映射。

以下代码包含三个选项。

  • 以大小写为键的字典和以参数标签为键的包含字典
  • 将每个关联值编码/解码为单独的键/值对
  • 使用任意键对数组中的所有关联值进行编码/解码。

首先枚举不能符合Codable

enum PostType {
    case fast(value: Int, value2: Int)
    case middle(bool: Bool)
    case slow(string: String, string2: String)
}

案例fast 使用一个数组,middle 一个子字典,slow 单独的键/值对。


然后声明MyStruct结构体,采用Codable并声明type

struct MyStruct : Codable {
    var type : PostType

此解决方案需要自定义键

  enum CodingKeys: String, CodingKey {
      case value, string, string2, middle
  }

encode 方法switches 用于案例并创建适当的类型

  func encode(to encoder: Encoder) throws {
     var container = encoder.container(keyedBy: CodingKeys.self)
      switch type {
      case .fast(let value, let value2) :
          try container.encode([value, value2], forKey: .value)

      case .slow(let string, let string2) :
          try container.encode(string, forKey: .string)
          try container.encode(string2, forKey: .string2)

      case .middle(let bool):
          try container.encode(["bool" : bool], forKey: .middle)
      }
  }

在decode 方法中,您可以通过传递的键区分大小写,确保它们是唯一的。

  init(from decoder: Decoder) throws {
      let values = try decoder.container(keyedBy: CodingKeys.self)
      let allKeys = values.allKeys
      if allKeys.contains(.middle) {
          let value = try values.decode([String:Bool].self, forKey: .middle)
          type = PostType.middle(bool: value["bool"]!)
      } else if allKeys.contains(.value) {
          let value = try values.decode([Int].self, forKey: .value)
          type = PostType.fast(value: value[0], value2: value[1])
      } else {
          let string = try values.decode(String.self, forKey: .string)
          let string2 = try values.decode(String.self, forKey: .string2)
          type = PostType.slow(string: string, string2: string2)
      }
   }
}

虽然有些键是硬编码的,但第一个选项似乎是最合适的。


最后是一个使用它的例子:

let jsonString = "[{\"value\": [2, 6]}, {\"string\" : \"foo\", \"string2\" : \"bar\"}, {\"middle\" : {\"bool\" : true}}]"

let jsonData = jsonString.data(using: .utf8)!

do {
    let decoded = try JSONDecoder().decode([MyStruct].self, from: jsonData)
    print("decoded:", decoded)

    let newEncoded = try JSONEncoder().encode(decoded)
    print("re-encoded:", String(data: newEncoded, encoding: .utf8)!)

} catch {
    print(error)
}

【讨论】:

    猜你喜欢
    • 2017-11-16
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 2021-06-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-05
    相关资源
    最近更新 更多