开发者社区> 问答> 正文

如果单元素解码失败,Swift JSONDecode解码数组也会失败

在使用Swift4和Codable协议时,我遇到了以下问题-似乎没有办法允许JSONDecoder跳过数组中的元素。例如,我具有以下JSON:

[ { "name": "Banana", "points": 200, "description": "A banana grown in Ecuador." }, { "name": "Orange" } ] 和一个可编码的结构:

struct GroceryProduct: Codable { var name: String var points: Int var description: String? } 解码此json时

let decoder = JSONDecoder() let products = try decoder.decode([GroceryProduct].self, from: json) 结果products为空。这是可以预期的,因为JSON中的第二个对象没有"points"键,而points在GroceryProductstruct中不是可选的。

问题是如何允许JSONDecoder“跳过”无效对象? 问题来源于stack overflow

展开
收起
保持可爱mmm 2020-02-08 12:11:47 1696 0
1 条回答
写回答
取消 提交回答
  • 一种选择是使用包装器类型,尝试对给定值进行解码。nil如果不成功,则存储:

    struct FailableDecodable

    : Decodable {
    let base: Base?
    
    init(from decoder: Decoder) throws {
        let container = try decoder.singleValueContainer()
        self.base = try? container.decode(Base.self)
    }
    

    } 然后,我们可以解码这些数组,并GroceryProduct在Base占位符中填写以下内容:

    import Foundation

    let json = """ [ { "name": "Banana", "points": 200, "description": "A banana grown in Ecuador." }, { "name": "Orange" } ] """.data(using: .utf8)!

    struct GroceryProduct : Codable { var name: String var points: Int var description: String? }

    let products = try JSONDecoder() .decode([FailableDecodable ].self, from: json) .compactMap { $0.base } // .flatMap in Swift 4.0

    print(products)

    // [ // GroceryProduct( // name: "Banana", points: 200, // description: Optional("A banana grown in Ecuador.") // ) // ] 然后,我们.compactMap { $0.base }用于过滤掉nil元素(那些在解码时引发错误的元素)。

    这将创建一个的中间数组[FailableDecodable ],这不应该成为问题。但是,如果您希望避免这种情况,则可以始终创建另一个包装器类型,该包装器类型将对来自无键容器的每个元素进行解码和解包:

    struct FailableCodableArray : Codable {

    var elements: [Element]
    
    init(from decoder: Decoder) throws {
    
        var container = try decoder.unkeyedContainer()
    
        var elements = [Element]()
        if let count = container.count {
            elements.reserveCapacity(count)
        }
    
        while !container.isAtEnd {
            if let element = try container
                .decode(FailableDecodable<Element>.self).base {
    
                elements.append(element)
            }
        }
    
        self.elements = elements
    }
    
    func encode(to encoder: Encoder) throws {
        var container = encoder.singleValueContainer()
        try container.encode(elements)
    }
    

    } 然后,您将解码为:

    let products = try JSONDecoder() .decode(FailableCodableArray .self, from: json) .elements

    print(products)

    // [ // GroceryProduct( // name: "Banana", points: 200, // description: Optional("A banana grown in Ecuador.") // ) // ]

    2020-02-08 12:12:09
    赞同 展开评论 打赏
问答排行榜
最热
最新

相关课程

更多

相关电子书

更多
OpenStack Swift 海量小文件优化之路 立即下载
From Java/Android to Swift iOS 立即下载
Swift在Airbnb的应用实践 立即下载