The following 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}}Copy the code

Parsing encounters two problems:

  1. How to combine separated keys and values (Key in fields, value in items)
  2. How to handle different types of arrays (items have String and Float types)

Of course, this piece of JSON is also a headache for me, and it took me a lot of time to deal with it. If it was written by a colleague, I will hammer him to death and ask him to change the structure

Swift’s Codable parsing JSON is the most elegant solution I’ve found, without the assignment process. At the same time, you can enjoy constant optimization from the bottom, more comfortable than SwiftJson, of course, you can use whatever you like, next research how to parse

The final code

import Foundation

typealias Fields = Array<String>
typealias Items =  Array<Array<MetadataType>>

extension Data {
    /// json data to model
    // You can convert the network request to a successful response directly
    func jsonDataMapModel<T: Decodable> (_ type: T.Type) -> T? {
        let decoder = JSONDecoder(a)do {
            return try decoder.decode(T.self, from: self)}catch {
            print("Parsing failed\ [self)")
            return nil}}}extension String {
    /// json string to model
    func jsonStringMapModel<T: Decodable> (_ type: T.Type) -> T? {
        
        if let jsonData = self.data(using: .utf8) {
            return jsonData.jsonDataMapModel(T.self)}print("Parsing failed\ [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)
    }
  }
}

// Layer 3 Model definition

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
    // The core code of the solution uses computed attributes to solve the problem of data aggregation, and uses generics to solve the problem of multiple types in arrays
    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}}}// Final target structure
struct DailyModel: Decodable {
    var ts_code:String? = nil
    var trade_date:String? = nil
    var open:Float? = nil
}
Copy the code

Test code:

func test(a){
    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 ", "> < span style =" max-width: 100%; clear: both;
    let model = jsonString.jsonStringMapModel(WDTRootModel<DailyModel>.self)
    print(model?.data?.items?[0] ?? No value "test")
    print(model?.data?.realItems?[0]?.ts_code ?? "The name no value.")
}

test()
Copy the code

All right, let’s copy it into the playground and try it