2017-06-16 79 views
2

注意:我已經看過了這個問題 - >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成爲那個價值。 –

回答

5

由於具有關聯類型的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) 
}