如下的Json
{
"code": 0,
"msg": "",
"data": {
"fields": [
"ts_code",
"trade_date",
"open"
],
"items": [
[
"000001.SZ",
"20210326",
20.84
],
[
"600000.SH",
"20210326",
10.68
]
],
"has_more": false
}
}
解析会遇到两个问题:
- 如何把分离的Key和value组合 (key在fields里,value在items里)
- 如何处理数组中不同的类型 (items里的数据类型有String和Float两种)
当然这段json对我来说也很头痛,花了不少时间处理,如果是同事写的,我会锤死他之前让他改一下结构
使用Swift提供的Codable解析json是我觉得最优雅的方案,无需赋值过程。同时能够享受到来自底层的不断优化,比SwiftJson要舒服一些,当然你喜欢用什么都可以,接下来研究一下如何解析
最终代码
import Foundation
typealias Fields = Array<String>
typealias Items = Array<Array<MetadataType>>
extension Data {
/// json data 转模型
/// 可以直接拿网络请求成功的response直接转换
func jsonDataMapModel<T: Decodable>(_ type: T.Type) -> T? {
let decoder = JSONDecoder()
do {
return try decoder.decode(T.self, from: self)
} catch {
print("解析失败\(self)")
return nil
}
}
}
extension String {
/// json字符串转模型
func jsonStringMapModel<T: Decodable>(_ type: T.Type) -> T? {
if let jsonData = self.data(using: .utf8) {
return jsonData.jsonDataMapModel(T.self)
}
print("解析失败\(self)")
return nil
}
}
enum MetadataType: Codable {
case float(Float)
case string(String)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
self = try .float(container.decode(Float.self))
} catch DecodingError.typeMismatch {
do {
self = try .string(container.decode(String.self))
} catch DecodingError.typeMismatch {
throw DecodingError.typeMismatch(MetadataType.self, DecodingError.Context(codingPath: decoder.codingPath, debugDescription: "Encoded payload not of an expected type"))
}
}
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch self {
case .float(let float):
try container.encode(float)
case .string(let string):
try container.encode(string)
}
}
}
//3层 Model定义
struct WDTRootModel<T: Decodable>: Decodable {
var code:Int? = nil
var msg:String? = nil
var data:WDTDataModel<T>? = nil
}
struct WDTDataModel<T: Decodable>: Decodable {
var fields: Fields? = nil
var items: Items? = nil
//方案核心代码 使用计算属性来解决数据聚合的问题,使用泛型解决数组中多类型的问题
var realItems:[T?]?{
items!.map { (values) -> T? in
let dic = Dictionary.init(uniqueKeysWithValues: zip(fields!, values.map({ (md) -> Any in
switch md {
case .float(let res):
return res
case .string(let res):
return res
}
})))
if let data = try? JSONSerialization.data(withJSONObject: dic, options: []) {
return data.jsonDataMapModel(T.self)
}
return nil
}
}
}
//最终目标结构
struct DailyModel: Decodable {
var ts_code:String? = nil
var trade_date:String? = nil
var open:Float? = nil
}
测试代码:
func test(){
let jsonString = """
{
"code": 0,
"msg": "",
"data": {
"fields": [
"ts_code",
"trade_date",
"open",
"high",
"low",
"close",
"pre_close",
"change",
"pct_chg",
"vol",
"amount"
],
"items": [
[
"000001.SZ",
"20210326",
20.84,
21.4,
20.76,
21.14,
20.75,
0.39,
1.8795,
822108.06,
1733412.579
],
[
"600000.SH",
"20210326",
10.68,
10.73,
10.61,
10.62,
10.63,
-0.01,
-0.0941,
408827.34,
435739.848
]
],
"has_more": false
}
}
"""
let model = jsonString.jsonStringMapModel(WDTRootModel<DailyModel>.self)
print(model?.data?.items?[0] ?? "test无值")
print(model?.data?.realItems?[0]?.ts_code ?? "name无值")
}
test()
好了,copy到playground里试一下吧