Swift 转模型 Codable和ObjectMapper 使用比较

代码基于Swift4.0.

导读,例子中用到的book.json文件
{
    "title": "War and Peace: A protocol oriented approach to diplomacy",
    "author": "A. Keed Decoder",
    "rating": null,
    "publishedAt": "019-04-16T9:24:37TPM.000Z"
}

使用差异

一、 是否需要映射字段

原生

  • 属性名和json返回的字段名完全一样则无需添加映射
  • 属性名和json返回的字段名只要有一个不同,则需要用 enum CodingKey全部添加映射

Mapper

  • 即使属性名和json返回的字段名完全一样,也必须用func mapping(map: Map)添加映射。 否则可选的属性为nil。
import UIKit
import ObjectMapper

struct XXBook: Mappable {
    // 通过Mapper转模型
    var title: String?
    var author: String?
    var rating: Float?
    var publishedAt: String?
    
    init?(map: Map) {
        
    }
    
    mutating func mapping(map: Map) {
        title            <- map["title"]
        author           <- map["author"]
        rating           <- map["rating"]
        publishedAt      <- map["publishedAt"]
    }
    
}


struct XXXBook: Codable {
    // 通过原生转模型
    var title: String?
    var author: String?
    var rating: Float?
    var publishedAt: String?
}

二、 转模型

原生

  • 通过 JSONDecoder Data
//用原生方式转模型
        guard let path = Bundle.main.path(forResource: "book", ofType: "json") else {
            return
        }
        
        guard let jsonData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) else {
            return
        }
        
        let decoder = JSONDecoder()
        if let book = try? decoder.decode(XXXBook.self, from: jsonData) {
            print(book)
        } else {
            print("decode failed")
        }

Mapper

  • 通过 JSONSerialization
        guard let path = Bundle.main.path(forResource: "book", ofType: "json") else {
            return
        }
        
        guard let jsonData = try? Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) else {
            return
        }
        
        guard let jsonDict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) else {
            return
        }
        
        guard let json = jsonDict as? [String: Any] else {
            return
        }
        // 用ObjectMapper转模型
        if let book = XXBook(JSON: json) {
            print(book)
        } else {
            print("转模型 failed")
        }
三、 解析字段

原生

  • 解析json中返回带null的数据,如导读的rating所示,属性为nil
XXBook(title: Optional("War and Peace: A protocol oriented approach to diplomacy"), author: Optional("A. Keed Decoder"), rating: nil, publishedAt: Optional("019-04-16T9:24:37TPM.000Z"))
  • 解析json中返回数据类型不一致,如将rating 改为String类型返回"rating": "5.0",属性为nil
XXBook(title: Optional("War and Peace: A protocol oriented approach to diplomacy"), author: Optional("A. Keed Decoder"), rating: nil, publishedAt: Optional("019-04-16T9:24:37TPM.000Z"))

Mapper

  • 解析json中返回带null的数据,如导读的rating所示,属性为nil(和原生结果一样)
XXXBook(title: Optional("War and Peace: A protocol oriented approach to diplomacy"), author: Optional("A. Keed Decoder"), rating: nil, publishedAt: Optional("019-04-16T9:24:37TPM.000Z"))
  • 解析json中返回数据类型不一致,如将rating 改为string类型返回"rating": "5.0",整个Book对象解析失败decode failed
decode failed

解析特殊类型差异

一、 日期类型

首先我们需要改造book.json, 和XXBookXXXBook类型
book.json填入正确的时间格式

{
    "title": "War and Peace: A protocol oriented approach to diplomacy",
    "author": "A. Keed Decoder",
    "rating": 5.0,
    "publishedAt": "2018-01-01T00:00:00.000Z"
}

XXBookXXXBookpublishedAt字段改为Date

struct XXBook: Mappable {
    // 通过Mapper转模型
    var title: String?
    var author: String?
    var rating: Float?
    var publishedAt: Date?
    
    init?(map: Map) {
        
    }
    
    mutating func mapping(map: Map) {
        title            <- map["title"]
        author           <- map["author"]
        rating           <- map["rating"]
        publishedAt      <- (map["publishedAt"], ObjectMapperDateTransform())  //mapper转换日期格式 需要自定义变换类需要实现 TransformType 协议。
    }
}

struct ObjectMapperDateTransform: TransformType {
    public typealias Object = Date
    public typealias JSON = String
    public init() {}
    public func transformFromJSON(_ value: Any?) -> Date? {
        if let timeStr = value as? String {
            return XXDataConvert.stringConvertDate(string: timeStr)
            
        }
        return nil
    }
    public func transformToJSON(_ value: Date?) -> String? {
        if let date = value {
            return XXDataConvert.dateConvertString(date: date)
        }
        return nil
    }
}

struct XXXBook: Codable {
    // 通过原生转模型
    var title: String?
    var author: String?
    var rating: Float?
    var publishedAt: Date?
}

struct XXDataConvert {
// 为Mapper作日期转换时抽了几个方法
    static func dateFormatter() -> DateFormatter {
        let dateFormatter = DateFormatter()
        dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
        dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
        return dateFormatter
    }
    
    static func dateConvertString(date: Date) -> String? {
        let formatter = dateFormatter()
        let date = formatter.string(from: date)
        return date.components(separatedBy: " ").first
    }
    
    static func stringConvertDate(string: String) -> Date? {
        let dateFormat = dateFormatter()
        return dateFormat.date(from: string)
    }
}

原生

  • 解析Date只需要为decoder加一句代码
decoder.dateDecodingStrategy = .formatted(XXDataConvert.dateFormatter())

Mapper

  • 解析Date,需要自定义一个遵守TransformType协议的变换结构体(/类),并且实现TransformType 协议中必须要实现的方法。
public protocol TransformType {
    associatedtype Object
    associatedtype JSON

    func transformFromJSON(_ value: Any?) -> Object?
    func transformToJSON(_ value: Object?) -> JSON?
}
二、 枚举类型

XXBookXXXBook 中新增字段 authorGender。 类型是 enum XXGender

enum XXGender: String, Codable {
    case male
    case female
    case other
}

原生

  • 解析authorGender需要写原值类型并遵循Codable协议。 如果解析字段和返回的json字段一致。解析时则什么都不用做了
var authorGender: XXGender?

Mapper

  • 解析authorGender需在mapping方法中加上
authorGender      <- (map["authorGender"], EnumTransform())

总结

  • 在使用原生解析非常方便,字段一致不用写字段映射,对Date,Enum的兼容也很好。只是如果后台返回的字段类型和解析时属性的类型不一致时会导致整个对象解析失败。代价很大。
  • Mapper 必须为每个字段映射即使和后台返回的字段名完全一样,添加特殊类型的解析略微复杂。但是会后台返回的数据兼容较好。

在项目中还是结合自己的需求做选择。 像本人负责的项目中,同事无法接受如果后台返回的类型不一致而使对象解析失败,所以坚决选择用ObjectMapper了。

你可能感兴趣的:(Swift 转模型 Codable和ObjectMapper 使用比较)