History of Swift JSON

  1. I used it in the beginningNSJSONSerializationConvert to dictionaries and arrays to use!
  2. Later apple usedSwiftreimplementedJSONSerializationCan avoid usingNSArrayandNSDictionaryTo bridge and improve parsing efficiency.
  3. And then a lot of three partiesJSONLibraries appear one after another, such as:SwiftyJSON,HandyJSON. Please forgive me for not using these tripartite libraries. Although I have reference learning, I have been maintaining and improving the JSON library encapsulated by myself, which I have used during this periodJSONThe simplicity has improved, but I still find it troublesome.
  4. fromSwift, 4.0At first, Apple providedCodableThe protocol andJSONDecoder.JSONEncoder, an improvement that eclipses a number of tripartite libraries, willJSONThe convenient way to the model has been promoted to a new height, but this way is still too tough, the model type is too rigid, a mistake will lead to parsing failure because of type inconsistency, so I put my ownJSONThe library to addCodableProtocol support as a useful addition to the official library.
  5. inSwift, 4.2whendynamicMemberLookupAdding (dynamic properties) to (soft)JSONThe model got a shot in the arm.

Example JSON

{“name”:” wang “,”age”:5,”reads”:[” Grimm “,” Andersen’s Fairy Tale “]}

Defects of the previous tripartite JSON library

In the previous tripartite JSON library, when parsing relatively dynamic JSON data into the model, in addition to writing the property type, in the constructor, also need to write the corresponding key, the model is very verbose to write, for example:

struct User {
    var name:String
    var age:Int
    var reads:[String]
    
    init(_ json:JSON) {
        name = json["name"].string
        age = json["age"].int
        reads = json["reads"].array
    }
}
Copy the code

The more attributes you have, the more you write in the constructor, and the same key string and variable name becomes very verbose


The defect of JSONDecoder

Since Apple revolutionized the Codable protocol, you now only need to write model properties, not constructors

struct User: Codable {
    var name:String
    var age:Int
    var reads:[String]}Copy the code

It’s also very easy to parse

let user = try! JSONDecoder().decode(User.self, from: data)
Copy the code

It may be because Apple, as a large company, has extremely strict coding specifications and front-end collaboration standards, so JSONDecoder has very strict requirements for type, if replaced with the following JSON content will fail parsing:

{“name”:” wang “,”age”:”5″,”reads”:[” Greene’s Fairy Tales “,” Anderson’s Fairy Tales “]}

This changes the age type to a string “5” that does not match the Int type of the model, making the entire JSON unparsable. In actual development, the front and back ends may not have such strict specifications, nor can they always encounter a reliable back end, so often because of various types of reasons for failure to parse the model is very painful.


You may say that this is a minor problem, as long as the standard development is good, then the following problem is hard.


For example, the JSON returned by the back end usually has several fixed fields to determine the availability of the return result, for example:

{
    "errorCode":0."errorMessage":"Success"."result":{}
}
Copy the code
{
    "errorCode":1."errorMessage":"Missing parameters"."result":null
}
Copy the code

In the preceding example, errorCode is the error status returned by the server. 0 indicates that the request is successful. ErrorMessage is the error status message. If you want to write a model:

struct ResponseJSON: Codable {
    var errorMessage: String
    var errorCode: Int
    var result: ???????
}
Copy the code

This is awkward because the type of the result property cannot be filled in because it is not certain what model it should use at the moment. Solutions are as follows:

  1. Instead of using a model, use a dictionary, but then you go back to the original and it’s not convenient to use.
  2. resultAttribute type use[String:Any]Although the model is used at present, it is very inconvenient to use it later.
  3. ResponseJSONThe use of generic definitions is prepassed in by the call network request interfaceresultRequired model
  4. Defines a dynamic type to hold incomplete parsesJSONTo allow the corresponding interface to useresultFields are lazy when parsed to the appropriate model

Among them, the better methods are 3 and 4, but method 3 still has defects. Does the same API return the same JSON structure? There may be a Type attribute that indicates what structure the other attribute is. In such cases, prepassing generics is useless.

So we need to define a dynamic type that meets the Codable protocol to hold JSON that is not fully parsed into the desired model when needed.

public enum JSON: Codable {
    case object (Object)
    case array  (Array)
    case string (String)
    case number (Number)
    case bool   (Bool)
    case null
    case error  (Error, ignore:[String])}Copy the code

There is more to the code than that, of course; see the JSON section in basic.frameworks for details

  • Now, we can takeResponseJSONDefined as:
struct ResponseJSON: Codable {
    var errorMessage: String
    var errorCode: Int
    var result: JSON
}
Copy the code

Use as needed

let user = try! JSON.Decoder().decode(User.self, from: responseJSON.result)
Copy the code

Or some other model


  • Precedent (JSON)object,array,string,number,bool,nullThese six are familiar JSON data types, anderrorWhat is it? Why add it?

As the name implies, an error is an error, and an error type is added to retrieve an optional chain instead of an attribute.

  • If there is noerror, ourJSONThis is how it should be used.
var json:JSON=...letname = json.result? .list? [0].name?.string
Copy the code

Is this way using optional chains? If one of the properties doesn’t exist, you don’t get an error, you get a nil value, and that’s usually enough, but if you have a bad JSON, you need to look at it layer by layer or test it, and the error message is completely lost at what level in the optional chain.

Now that the error (error, ignore:[String]) type is added, there is no need to use optional chains to pass attributes

var json:JSON=...let name = json.result.list[0].name.string
Copy the code

When an attribute is not available, it can be given an Error type, and it can be passed on without losing the Error message. When converting a string or int, you can either interrupt or return the default value, depending on your development and production environment!

JSONDecoder and Json.decoder

  • JSONDecoderParsing binary JSON into a model is provided by AppleModelThe resolution of the class
  • JSON.DecoderIs (Basic.frameworks), the binary or JSON soft model is parsed into a model according to the official methodModelThe resolution of the class

  1. They do the same thing. It’s justJSON.DecoderWeakened data type mandates,
  2. JSON.DecoderOffers more than officialJSONDecoderMore parsing strategies,
  3. JSON.DecoderChanges to global policy defaults are provided for convenience.

To summarize

  1. DataorStringYou can go through the officialJSONDecoderOr (Basic.frameworks) in theJSON.DecoderParsing to implementationCodableOf the agreementModelmodel
  2. DataorStringCan be accessed through (Basic.frameworks) in theJSON.DecoderOr resolves toJSONSoft model,ModelA model or a mixture of both
  3. JSONThe soft model can be used by (Basic.frameworks) in theJSON.DecoderResolve toModelModel or hybrid model.
  4. ModelA model or hybrid model can be created by (Basic.frameworks) in theJSON.EncoderSeries intoJSONSoft model, orDatabinary
  5. ModelModels or hybrid models are available through the officialJSONEncoderSeries intoDatabinary
  6. JSONSoft models are available through the officialJSONEncoderOr (Basic.frameworks) in theJSON.EncoderSeries intoDatabinary

Example

See the article pure Swift Project -HTTP (Basic.Frameworks) for an example