如何使用可解码协议将此JSON转换为Swift结构?

如何使用可解码协议将此JSON转换为Swift结构?

问题描述:

注意:我已经看过了这个问题 - >How do I use custom keys with Swift 4's Decodable protocol?但它并没有解释如何编码/解码枚举如何使用可解码协议将此JSON转换为Swift结构?

这里是我想要的结构:

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

+0

这不是重复。这个问题是解码一个结构。这是解码一个元组的Enum。我知道如何解码结构,但与枚举是非常不同的。 –

+0

什么是您预期的关联枚举值的JSON表示? – vadian

+0

@vadian多数民众赞成的事情。我知道我希望我的结构看起来像'MyStruct',但我不知道JSON应该是什么样子。如果有人能告诉我,那么我可以让JSON成为那个价值。 –

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

以下代码包含三个选项。

  • 与本案作为密钥A字典,并用参数的标签作为键的含词典
  • 编码/解码每个相关值作为单独的键/值对
  • 编码/解码与阵列中的所有相关联的值一个任意的键。

所有枚举首先必须不符合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方法的情况下switch ES并创建相应类型

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) 
}