ios - JSONDecoder 解码不同类型的相同
问题描述
我正在使用 JSONDecoder 来解析我的 JSON 响应:
{"status":"1","errorCode":"0","msg":"","info":1}
或者
{"status":"1","errorCode":"0","msg":"","info":"a"}
或者
{"status":"1","errorCode":"0","msg":"","info":{"name":"a"}}
或者
{"status":"1","errorCode":"0","msg":"","info":{"text":"b"}}
或者
{"status":"1","errorCode":"0","msg":"","info":[{"name":"a"}]}
这是我的basejson
import Foundation
class BaseJson: Decodable {
var id = UUID()
var status: String
var errorCode: String
var msg: String
private enum CodingKeys: String, CodingKey {
case status = "status", errorCode = "errorCode", msg = "msg", info = "info"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decodeIfPresent(String.self, forKey: .status) ?? "0"
errorCode = try container.decodeIfPresent(String.self, forKey: .errorCode) ?? "0"
msg = try container.decodeIfPresent(String.self, forKey: .msg) ?? "1"
}
}
那么我应该怎么做才能让这个基本类解析所有结果
解决方案
您可以使用具有关联值的 Enum 来处理它。
但是正如 vadian 所说,如果可以的话,API 最好根据相同的参数始终响应相同类型的数据。
你可以添加这个:
enum Info: Decodable {
enum DecodingError: Error {
case unknownValueType
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let asInt = try? container.decode(Int.self) {
self = .intValue(asInt)
} else if let asString = try? container.decode(String.self) {
self = .stringValue(asString)
} else if let asDict = try? container.decode([String: String].self) {
self = .dictValue(asDict)
} else if let asArrayofDicts = try? container.decode([[String: String]].self) {
self = .arrayOfDictValue(asArrayofDicts)
} else {
throw Info.DecodingError.unknownValueType
}
}
case intValue(Int)
case stringValue(String)
case dictValue([String: String])
case arrayOfDictValue([[String: String]])
}
我使用[String: String]
and [[String: String]]
,但如果有限制,您也可以放置自己的自定义结构:
struct InfoText:可解码 { 让文本:字符串 }
结构信息名称:可解码 { 让名称:字符串 }
并做:
案例信息文本(信息文本)
如果让 asInfoText = 试试?container.decode(InfoText.self) { ... }
等等
完整的代码和文本(在 Plyagrounds 中复制粘贴):
class BaseJson: Decodable {
enum Info: Decodable {
enum DecodingError: Error {
case unknownValueType
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let asInt = try? container.decode(Int.self) {
self = .intValue(asInt)
} else if let asString = try? container.decode(String.self) {
self = .stringValue(asString)
} else if let asDict = try? container.decode([String: String].self) {
self = .dictValue(asDict)
} else if let asArrayofDicts = try? container.decode([[String: String]].self) {
self = .arrayOfDictValue(asArrayofDicts)
} else {
throw Info.DecodingError.unknownValueType
}
}
case intValue(Int)
case stringValue(String)
case dictValue([String: String])
case arrayOfDictValue([[String: String]])
}
var id = UUID()
var status: String
var errorCode: String
var msg: String
var info: Info
private enum CodingKeys: String, CodingKey {
case status = "status", errorCode = "errorCode", msg = "msg", info = "info"
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
status = try container.decode(String.self, forKey: .status)
errorCode = try container.decode(String.self, forKey: .errorCode)
msg = try container.decode(String.self, forKey: .msg)
info = try container.decode(Info.self, forKey: .info)
}
static func test() {
let jsonStr = """
[
{"status":"1","errorCode":"0","msg":"","info":1},
{"status":"1","errorCode":"0","msg":"","info":"a"},
{"status":"1","errorCode":"0","msg":"","info":{"name":"a"}},
{"status":"1","errorCode":"0","msg":"","info":{"text":"b"}},
{"status":"1","errorCode":"0","msg":"","info":[{"name":"a"}]}
]
"""
let jsonData = jsonStr.data(using: .utf8)!
do {
let bases = try JSONDecoder().decode([BaseJson].self, from: jsonData)
print(bases)
for aBase in bases {
var infoStr = ""
switch aBase.info {
case .arrayOfDictValue(let array):
infoStr = "\(array)"
case .dictValue(let dictt):
infoStr = "\(dictt)"
case .intValue(let intV):
infoStr = "\(intV)"
case .stringValue(let str):
infoStr = str
}
print("Got: \(aBase.id), status: \(aBase.status), errorCode: \(aBase.errorCode), msg: \(aBase.msg), info: \(aBase.info) - badStringConversion for understanding: \(infoStr)")
}
} catch {
print("Error: \(error)")
}
}
}
BaseJson.test()
输出:
$> [__lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson, __lldb_expr_75.BaseJson]
$> Got: C92A4315-413C-4DA3-9D0B-E6CE3B081AF3, status: 1, errorCode: 0, msg: , info: intValue(1) - badStringConversion for understanding: 1
$> Got: 1AB9FE74-E282-4AC9-97B1-814DD5C9F412, status: 1, errorCode: 0, msg: , info: stringValue("a") - badStringConversion for understanding: a
$> Got: F082FD0D-818D-4A6F-906A-3A80D911C2BA, status: 1, errorCode: 0, msg: , info: dictValue(["name": "a"]) - badStringConversion for understanding: ["name": "a"]
$> Got: 2424AFD2-8417-4FCB-B97A-D03AB30A9C45, status: 1, errorCode: 0, msg: , info: dictValue(["text": "b"]) - badStringConversion for understanding: ["text": "b"]
$> Got: 05DDB7CB-DD0E-41BB-B878-F1AEBF0D6F43, status: 1, errorCode: 0, msg: , info: arrayOfDictValue([["name": "a"]]) - badStringConversion for understanding: [["name": "a"]]