首页 > 解决方案 > 是否可以使用 Swift/Codable 解码非同质数组?

问题描述

我需要解码数组中的 JSON 数据,其中第一个元素是与以下所有对象不同的对象。我想丢弃第一个数组元素并解码其余元素。Swift Codable 可以做到这一点吗?

这是我在 Swift Playground 中整理的一个示例,它说明了正在发生的事情

import Foundation
class Test: Codable {
    var name:String = ""
    var number:Int = -1
}
var json1 = """
[[{"Type":"Testing", "Name":23}],
[{"name":"Rec1", "number":2}],
[{"name":"Rec2", "number":3}]]
"""
var json2 = """
[[{"name":"Rec12", "number":22}],
[{"name":"Rec22", "number":32}]]
"""
do{
    let test1 = try JSONDecoder().decode([[Test]].self, from: json1.data(using: .utf8)!)
    for t in test1 {
        print("\(t[0].name) \(t[0].number)")
    }
}catch{
    print("Exception thrown 1: \(error)")
}
do{
    let test2 = try JSONDecoder().decode([[Test]].self, from: json2.data(using: .utf8)!)
    for t in test2 {
        print("\(t[0].name) \(t[0].number)")
    }
}catch{
    print("Exception thrown 2: \(error)")
}

输出不足为奇。解码json1抛出异常,解码json2不会:

Exception thrown 1: keyNotFound(CodingKeys(stringValue: "name", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0), _JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"name\", intValue: nil) (\"name\").", underlyingError: nil))
Rec12 22
Rec22 32

我无法控制这些数据,我不能要求供应商做得更好——如果只是。

我能看到的唯一方法是在将 JSON 数据提供给 JSONDecoder 之前对其进行文本处理。我是 Swift 的新手,我希望有比这更好的方法......

private func _hackJSONArray(_ input:Data) throws -> Data {
    /*
     Input data is of the form:
     [
       [{"Type":"Testing", "Name":23}],
       [{"name":"Rec1", "number":2}],
       [{"name":"Rec2", "number":3}]
     ]
     
     That is the first element is not like the others
     */
    var instr = String(data:input, encoding:.utf8)!
    if instr.prefix(2) != "[[" {
        throw ErrorItem(theErrorType: .InvalidJSON(instr), theDescription: "Failed to decode JSON data.  Did not start with: \"[[\"")
    }
    if let endOfFirstElement = instr.firstIndex(of: "]") {
        let first = instr.index(instr.startIndex, offsetBy:1)
        let last = instr.index(endOfFirstElement, offsetBy: 2)
        instr.removeSubrange(first..<last)
    }else{
        throw ErrorItem(theErrorType: .InvalidJSON(instr), theDescription: "Failed to decode JSON data.  Could not find: \"]\"")
    }
    return instr.data(using: .utf8)!
}

标签: jsonswiftcodable

解决方案


所以,侧卫的答案是一个优雅的答案。但问题是它用不正确的数据填充了第一个数组的元素。作者说应该比被映射的更可能被扔掉。因此,在我看来,不是一个优雅但直接的解决方案是将您的数据结构字段声明为Optional

struct Test: Codable {
    var name: String?
    var number: Int?
}

然后检查你的结果是否有 nil 值:

testArray.filter({obj in
    return obj.name == nil ? false : true
})

推荐阅读