2017-08-17 53 views
2

我試圖用斯威夫特4的新JSON解碼從遠程服務器解析JSON。 JSON模式包含枚舉值,其中一些我實際上並不需要用於我的目的,我想忽略它。另外,我還想足夠強大,以便在JSON模式發生變化時,我仍然能夠儘可能多地讀取數據。任何方式夫特4 JSON解碼器不拋出含有無法識別的枚舉值數組?

問題是,當我嘗試解析包含枚舉的任何東西的數組時,除非每個枚舉值完全匹配我的枚舉的文字,否則解碼器會拋出異常而不是跳過它無法解析的數據。

這裏有一個簡單的例子:

enum Size: String, Codable { 
    case large = "large" 
    case small = "small" 
} 

enum Label: String, Codable { 
    case kitchen = "kitchen" 
    case bedroom = "bedroom" 
    case livingRoom = "living room" 
} 

struct Box: Codable { 
    var contents: String 
    var size: Size 
    var labels: [Label] 
} 

當我解析完全符合我的尺寸枚舉數據,我得到預期的結果:

let goodJson = """ 
[ 
    { 
    "contents": "pillows", 
    "size": "large", 
    "labels": [ 
     "bedroom" 
    ] 
    }, 
    { 
    "contents": "books", 
    "size": "small", 
    "labels": [ 
     "bedroom", 
     "living room" 
    ] 
    } 
] 
""".data(using: .utf8)! 

let goodBoxes = try? JSONDecoder().decode([Box?].self, from: goodJson) 
// returns [{{contents "pillows", large, [bedroom]}},{{contents "books", small, [bedroom, livingRoom]}}] 

但是,如果有內容,唐」 t符合枚舉,解碼器拋出一個異常,我什麼也沒有回來。

let badJson = """ 
[ 
    { 
    "contents": "pillows", 
    "size": "large", 
    "labels": [ 
     "bedroom" 
    ] 
    }, 
    { 
    "contents": "books", 
    "size": "small", 
    "labels": [ 
     "bedroom", 
     "living room", 
     "office" 
    ] 
    }, 
    { 
    "contents": "toys", 
    "size": "medium", 
    "labels": [ 
     "bedroom" 
    ] 
    } 
] 
""".data(using: .utf8)! 

let badBoxes = try? JSONDecoder().decode([Box?].self, from: badJson) // returns nil 

理想的情況下,在這種情況下,我想拿回2項,其中尺寸符合「小」或「大」,第二項傷口有2個有效的標籤,「臥室」和「生活房間」。

如果我實現我自己的init(來源:解碼器)的盒子,我可以解碼標籤自己扔出去的任何不符合我的枚舉。但是,我想不出如何解讀[專欄]類型忽略無效箱沒有實現我自己的解碼器和解析JSON自己,這違背了使用可編碼的目的。

任何想法?

+0

我有一個類似的情況,我只想忽略無效的數組元素,並且不希望解析器在整個數組上失敗。是否真的沒有辦法跳過無效的元素,而不是完全「拋出」數組,而不是完全解析? : -/ – d4Rk

+0

比較https://stackoverflow.com/q/46344963/2976878 – Hamish

回答

0

我承認,這是不是最漂亮的解決方案,但它是我想出了,我想我也有同感。我在Array上創建了一個允許這樣做的擴展。最大的障礙是你必須解碼並再次編碼JSON數據。

extension Array where Element: Codable { 
    public static func decode(_ json: Data) throws -> [Element] { 
     let jsonDecoder = JSONDecoder() 
     var decodedElements: [Element] = [] 
     if let jsonObject = (try? JSONSerialization.jsonObject(with: json, options: [])) as? Array<Any> { 
      for json in jsonObject { 
       if let data = try? JSONSerialization.data(withJSONObject: json, options: []), let element = (try? jsonDecoder.decode(Element.self, from: data)) { 
        decodedElements.append(element) 
       } 
      } 
     } 
     return decodedElements 
    } 
} 

您可以使用該擴展與任何符合Codable,並應解決您的問題。

[Box].decode(json) 

不幸的是我沒有看到這將如何解決這個問題有標籤是不正確的,你必須做像你說的,並覆蓋init(from: Decoder),以確保您的標籤是有效的。