有一些JSON格式,其中的属性有时是字典,有时是数组。
关于这个问题有几个问题。这个问题是关于良好的错误处理。这个问题从Hot to decode JSON data that could and array or a single element in Swift?的可接受答案结束的地方开始。
变量数据
如果只有一个值,则名称数据以字典的形式出现:
{
"id": "item123",
"name": {
"@language": "en",
"@value": "Test Item"
}
}
字符串
如果有两个或多个值,则名称数据将以数组的形式出现:
{
"id": "item123",
"name": [
{
"@language": "en",
"@value": "Test Item"
},
{
"@language": "de",
"@value": "Testartikel"
}
]
}
型
这是我的解决方案,如果Decodable结构与要解码的JSON数据匹配,它就可以正常工作。
struct Item: Codable {
var id: String
var name: [LocalizedString]
enum CodingKeys: String, CodingKey {
case id
case name
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
if let singleName = try? container.decode(LocalizedString.self, forKey: .name) {
name = [singleName]
} else {
name = try container.decodeIfPresent([LocalizedString].self, forKey: .name) ?? []
}
}
}
struct LocalizedString: Codable {
var language: String
var value: String
enum CodingKeys: String, CodingKey {
case language = "@language"
case value = "@value"
}
}
型
(this与其他地方接受的答案相匹配)
它的缺点是,解码错误消息在这种方法中不再有用。
假设像这样的意外数据(@language丢失):
{
"id": "item123",
"name": {
"@value": "Test Item"
}
}
型
我收到一条错误消息
错误:typeMismatch(Swift.Array,Swift.DecodingError.Context(codingPath:[CodingKeys(stringValue:“name”,intValue:nil)],debugDescription:“期望解码Array,但却找到了字典。",underlyingError:nil))
而不是正确的错误消息,关键语言丢失。
正确的错误消息将与问号一起被丢弃,
if let singleName = try?
型
那么有没有一种方法可以先检查它是否是一个数组,然后使用拟合
container.decodeIfPresent(LocalizedString.self, forKey: .name)
型
或
container.decodeIfPresent([LocalizedString].self, forKey: .name)
型
在任何情况下都能得到一个好的错误消息?
我的真实的世界数据比这个简单的例子嵌套得多,所以当错误消息不合适时,很难找到错误。
下面是这个问题的一个完整的可测试的例子:
import XCTest
class ItemDecodingTests: XCTestCase {
func testDecodeItemWithSingleLocalizedString() throws {
let jsonData = test_json_data_single.data(using: .utf8)!
let item = try JSONDecoder().decode(Item.self, from: jsonData)
XCTAssertNotNil(item)
}
func testDecodeItemWithMultipleLocalizedStrings() throws {
let jsonData = test_json_data_array.data(using: .utf8)!
let item = try JSONDecoder().decode(Item.self, from: jsonData)
XCTAssertNotNil(item)
}
func testDecodeItemWithMissingLanguageProperty() throws {
let jsonData = test_json_data_missing_language.data(using: .utf8)!
XCTAssertThrowsError(try JSONDecoder().decode(Item.self, from: jsonData)) { error in
print("Error: \(error)")
}
}
}
// Test JSON Data
let test_json_data_single = """
{
"id": "item123",
"name": {
"@language": "en",
"@value": "Test Item"
}
}
"""
let test_json_data_array = """
{
"id": "item123",
"name": [
{
"@language": "en",
"@value": "Test Item"
},
{
"@language": "de",
"@value": "Testartikel"
}
]
}
"""
let test_json_data_missing_language = """
{
"id": "item123",
"name": {
"@value": "Test Item"
}
}
"""
struct Item: Codable {
var id: String
var name: [LocalizedString]
enum CodingKeys: String, CodingKey {
case id
case name
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(String.self, forKey: .id)
if let singleName = try? container.decode(LocalizedString.self, forKey: .name) {
name = [singleName]
} else {
name = try container.decodeIfPresent([LocalizedString].self, forKey: .name) ?? []
}
}
}
struct LocalizedString: Codable {
var language: String
var value: String
enum CodingKeys: String, CodingKey {
case language = "@language"
case value = "@value"
}
}
型
1条答案
按热度按时间xmd2e60i1#
您没有得到预期的错误消息,因为您故意忽略了
try?
的错误。更好的方法是
catch
,并在解码数组之前显式地重新抛出keyNotFound
错误字符串