This is the 15th day of my participation in the Gwen Challenge.More article challenges

preface

Whenever network request is required, data exchange is required, and it is unavoidable to convert JSON to Model.

JSON: JavaScript Object Notation(JavaScript Object Notation). Since JSON is actually a JavaScript product, it is the most front-end friendly, with almost no translation and goes straight through. Syntax is called.

Let’s look at a simple example:

var myObj, x;
myObj = { "name":"runoob", "alexa":10000, "site":null };
x = myObj.name;
Copy the code

Myobj.name can be called directly in JavaScript, which is not possible in other programming languages.

For example, Dictionary in Swift, Map in Java, Dart, and Kotlin, and dict in Python all correspond to JSON — the representation of key-value pairs.

But when we use it, we rarely think about a lot of assignments or references via key-value pairs. Why?

MyObj [“name”] this way, too rely on string hard coding, a careless handwriting typing error.

  • Error-prone handwriting

  • Trying hard

  • Poor read-write

  • Poor scalability

Based on the above reasons, in general, JSON data obtained by network request will be converted to Model once.

How do I parse JSON

Java uses reflection, Dart blocks reflection for performance reasons, and more automation tools are considered for conversion. In OC, I used YYModel. In Swift5, I recommend Swift for Codable protocol first

Why Codable protocol?

To answer this question, I can say it through my own experience using JSON to Model from Swift2.0 to 4.0.

Swift2.0 — Bronze Age

I started to contact Swift in about 2016. When I started from OC to Swift, I learned the logic of OC directly into Swift, so I still considered using YYModel when I changed to Model.

Being young and ignorant, I didn’t know there was a jSON-to-model tool for the pure Swift framework.

YYModel is a very good JSON to model tool in OC, but in Swift, it is not acclimated.

If you want to use YYModel, all model objects must inherit NSObject. Objects in the model basically use OC types, like arrays use NSArray, dictionaries use NSDictionary, and characters use NSString, because these are class types. However, in Swift, Array, Dictionary, String and other basic data types are all designed as structs, and different designs lead to different ideas used in the process of Model transformation. If these are not clear, inexplicable crash or error will be reported.

Luckily, I didn’t write a commercial App Swift2, or I would have been laughed off.

Swift3.0 — The Silver Age

When I was working on Swift3, I basically determined two frameworks — ObjectMapper and SwiftyJSON through reading materials and studying on my own.

SwiftyJSON isn’t exactly JSON parsing friendly, as it still has to be hard-coded to call it, so it’s more of a debugging tool.

So, more often than not, I consider using ObjectMapper. Let’s look at this example:

JSON (to avoid writing JSON repeatedly, we’ll use this as an example) :

{province: Wuhan, city: Wuhan, location: {lat: 30.60, LNG: 114.04}, name: XXXXXXX, address: XXXXXXX, detail: 1, area: hanyang, street_id: 3 a1fde420cbb9d4dab059d36, uid: 3 a1fde420cbb9d4dab059d36}Copy the code

This JSON has a location wrapped around it, and we’re going to use ObjectMapper to say:

Import ObjectMapper /// struct LatLngEntity: Mappable {//MARK:- attribute set var lat: Double? var lng: Double? //MARK:- Mapper init? (map: Map) { } mutating func mapping(map: Map) { lat <- map["lat"] lng <- map["lng"] } } struct PoiEntity: Mappable {//MARK:- var type: String? var city: String? var location: LatLngEntity? var name: String? var address: String? var detail: String? var area: String? var street_id: String? var uid : String? init? (map: Map) { } mutating func mapping(map: Map) { province <- map["province"] city <- map["city"] location <- map["location"] name <- map["name"] address <- map["address"] detail <- map["detail"] area <- map["area"] street_id <- map["street_id"] uid <- map["uid"] } }Copy the code

Its general thinking process is as follows:

The model class inherits the Mappable protocol, implements the protocol, and completes the one-to-one mapping relationship in mutating Func Mapping (map: map) method.

In the early days, when there were few JSON to Swift language models, < -map [] encoding was handwritten, as you can imagine, once the JSON attributes are many, it is also a hard coding hell.

It wasn’t until I learned about the JSONExport tool that things got better.

In addition, ObjectMapper needs to call different methods for array parsing and single element parsing in JSON, which makes it more complicated to consider using generic design BaseModel to connect data.

T(JSON: JSON) and Mapper

(). MapArray (JSONArray: JSONS) convert dictionaries and array dictionaries using Mapper

/// /// -parameter dict: data dictionary /// - Returns: required model func mappableWithDict<T: Mappable>(_ dict: [String: Any]?) -> T? { guard let JSON = dict else { return nil } return T(JSON: /// /// -parameter dicts: data dictionary array /// - dicts: data dictionary array /// - Returns: Func mappableWithDictArray<T: Mappable>(_ dicts: [[String: Any]]?) -> [T]? { guard let JSONS = dicts else { return nil } return Mapper<T>().mapArray(JSONArray: JSONS) }Copy the code

So the model must be designed in two ways:

Class Response<T: Mappable>: Mappable {var message: String? var result : T? var status : Int? required init? Func mapping(map: map) {} Map) { message <- map["message"] result <- map["result"] status <- map["status"] } } class ResponseArray<T: Mappable>: Mappable { var message : String? var result : [T]? var status : Int? required init? (map: Map) { } func mapping(map: Map) { message <- map["message"] result <- map["result"] status <- map["status"] } }Copy the code

Generally, the JSON format returned by the same background will be common. For example, in this code, message is the message of the transmitted request and response information, and status is the business service code. The only difference may be the result, which may be a JSON or a [JSON]. The encapsulation of network layer becomes complicated.

Even ObjectMapper’s Alamofire classification uses two sets of apis:

public func responseObject<T: BaseMappable>(queue: DispatchQueue? = nil, keyPath: String? = nil, mapToObject object: T? = nil, context: MapContext? = nil, completionHandler: @escaping (DataResponse<T>) -> Void) -> Self

public func responseArray<T: BaseMappable>(queue: DispatchQueue? = nil, keyPath: String? = nil, context: MapContext? = nil, completionHandler: @escaping (DataResponse<[T]>) -> Void) -> Self
Copy the code

Just when I thought I’d be using ObjectMapper as a jSON-to-Model utility class for the rest of Swift development. Swift4, Codable came out of the sky and shattered the landscape.

Tomorrow to continue

Due to the length of this article, I wrote here today, but Codable has plenty of examples to cover.

Let’s continue tomorrow, everyone!