ObjectMapper 4.4.2

ObjectMapper 4.4.2

测试已测试
语言语言 SwiftSwift
许可 MIT
发布上次发布2024年4月
SPM支持 SPM

Tristan Himmelman维护。



  • 作者:
  • Tristan Himmelman

ObjectMapper

CocoaPods Carthage compatible Swift Package Manager Build Status

ObjectMapper 是一个用 Swift 编写的框架,它使您能够轻松地将您的模型对象(类和结构体)与 JSON 进行动态转换。

特性

  • 将 JSON 映射到对象
  • 将对象映射到 JSON
  • 嵌套对象(独立、数组中或字典中)
  • 映射过程中的自定义转换
  • 支持结构体
  • 支持不可变类型

基础知识

为了支持映射,一个类或结构体只需要实现 Mappable 协议,该协议包括以下函数

init?(map: Map)
mutating func mapping(map: Map)

ObjectMapper 使用 <- 操作符来定义每个成员变量如何映射到和从 JSON 中。

class User: Mappable {
    var username: String?
    var age: Int?
    var weight: Double!
    var array: [Any]?
    var dictionary: [String : Any] = [:]
    var bestFriend: User?                       // Nested User object
    var friends: [User]?                        // Array of Users
    var birthday: Date?

    required init?(map: Map) {

    }

    // Mappable
    func mapping(map: Map) {
        username    <- map["username"]
        age         <- map["age"]
        weight      <- map["weight"]
        array       <- map["arr"]
        dictionary  <- map["dict"]
        bestFriend  <- map["best_friend"]
        friends     <- map["friends"]
        birthday    <- (map["birthday"], DateTransform())
    }
}

struct Temperature: Mappable {
    var celsius: Double?
    var fahrenheit: Double?

    init?(map: Map) {

    }

    mutating func mapping(map: Map) {
        celsius 	<- map["celsius"]
        fahrenheit 	<- map["fahrenheit"]
    }
}

一旦您的类实现了 Mappable,ObjectMapper 允许您轻松地将对象与 JSON 进行转换。

将JSON字符串转换为模型对象

let user = User(JSONString: JSONString)

将模型对象转换为JSON字符串

let JSONString = user.toJSONString(prettyPrint: true)

或者,可以使用Mapper.swift类来完成上述操作(它还提供了其他情况的额外功能)

// Convert JSON String to Model
let user = Mapper<User>().map(JSONString: JSONString)
// Create JSON String from Model
let JSONString = Mapper().toJSONString(user, prettyPrint: true)

ObjectMapper可以映射以下类型的类

  • Int
  • Bool
  • Double
  • Float
  • String
  • RawRepresentable(枚举)
  • Array<Any>
  • Dictionary<String, Any>
  • Object<T: Mappable>
  • Array<T: Mappable>
  • Array<Array<T: Mappable>>
  • Set<T: Mappable>
  • Dictionary<String, T: Mappable>
  • Dictionary<String, Array<T: Mappable>>
  • 所有上述的可选类型
  • 上述类型的隐式解包可选类型

Mappable 协议

mutating func mapping(map: Map)

此函数是应当放置所有映射定义的地方。在解析JSON后,此函数会在对象创建成功后执行。在生成JSON时,这是唯一在对象上被调用的函数。

init?(map: Map)

这个可失败的初始化器由ObjectMapper用于对象创建。开发人员可以使用它来在对象序列化之前验证JSON。在函数中返回nil将阻止映射发生。您可以检查保存在Map对象中的JSON来进行验证。

required init?(map: Map){
	// check if a required "name" property exists within the JSON.
	if map.JSON["name"] == nil {
		return nil
	}
}

StaticMappable 协议

StaticMappableMappable的替代方案。它为开发者提供了一个静态函数,用于ObjectMapper对象初始化,而不是使用init?(map: Map)

注意:StaticMappable,就像Mappable一样,是BaseMappable的子协议,在BaseMappable中定义了mapping(map: Map)函数。

静态 func objectForMapping(map: Map) -> BaseMappable?

ObjectMapper 使用这个函数来获取用于映射的对象。开发者在函数中应返回一个符合 BaseMappable 的对象的实例。此函数还可以用于

  • 在对象序列化之前验证 JSON
  • 提供可用于映射的现有缓存对象
  • 返回用于映射的其他类型(也符合 BaseMappable)的对象。例如,您可能需要检查 JSON 以推断应使用哪种类型的对象进行映射(请参阅 ClassClusterTests.swift 中的示例

如果您需要在扩展中实现 ObjectMapper,则需要采用此协议而不是 Mappable 协议。

ImmutableMappable 协议

ImmutableMappable 提供了映射不可变属性的能力。这就是 ImmutableMappableMappable 的不同之处

ImmutableMappable Mappable
属性
let id: Int
let name: String?
var id: Int!
var name: String?
JSON -> 模型
init(map: Map) throws {
  id   = try map.value("id")
  name = try? map.value("name")
}
mutating func mapping(map: Map) {
  id   <- map["id"]
  name <- map["name"]
}
模型 -> JSON
func mapping(map: Map) {
  id   >>> map["id"]
  name >>> map["name"]
}
mutating func mapping(map: Map) {
  id   <- map["id"]
  name <- map["name"]
}
初始化
try User(JSONString: JSONString)
User(JSONString: JSONString)

init(map: Map) throws

此可抛出初始化器用于映射给定 Map 的不可变属性。每个不可变属性都应该在此初始化器中初始化。

  • Map 无法获取给定键的值时抛出错误
  • Map 在使用 Transform 转换值时失败

ImmutableMappable 使用 Map.value(_:using:) 方法从 Map 获取值。此方法应该使用 try 关键字,因为它可能会抛出错误。可以使用 try? 容易地处理可选属性。

init(map: Map) throws {
    name      = try map.value("name") // throws an error when it fails
    createdAt = try map.value("createdAt", using: DateTransform()) // throws an error when it fails
    updatedAt = try? map.value("updatedAt", using: DateTransform()) // optional
    posts     = (try? map.value("posts")) ?? [] // optional + default value
    surname    = try? map.value("surname", default: "DefaultSurname") // optional + default value as an argument
}

mutating func mapping(map: Map)

该方法执行反向转换操作(模型转换为JSON)。由于不可变属性无法与<-运算符进行映射,因此开发者必须使用>>>运算符定义反向转换。

mutating func mapping(map: Map) {
    name      >>> map["name"]
    createdAt >>> (map["createdAt"], DateTransform())
    updatedAt >>> (map["updatedAt"], DateTransform())
    posts     >>> map["posts"]
}

嵌套对象的简单映射

ObjectMapper支持在键中使用点表示法以方便地映射嵌套对象。给定以下JSON字符串

"distance" : {
     "text" : "102 ft",
     "value" : 31
}

您可以使用以下方式访问嵌套对象

func mapping(map: Map) {
    distance <- map["distance.value"]
}

嵌套键也支持从数组中访问值。给定一个包含距离数组的JSON响应,值可以按以下方式访问

distance <- map["distances.0.value"]

如果您有一个包含.的键,您可以选择禁用上述功能,如下所示

func mapping(map: Map) {
    identifier <- map["app.identifier", nested: false]
}

当您有包含.的嵌套键时,您可以通过以下方式传递自定义的嵌套键分隔符(#629

func mapping(map: Map) {
    appName <- map["com.myapp.info->com.myapp.name", delimiter: "->"]
}

自定义转换

ObjectMapper也支持自定义转换,在映射过程中转换值。要使用转换,只需在<-运算符右侧创建一个包含map["field_name"]和所选转换的元组即可。

birthday <- (map["birthday"], DateTransform())

上述转换将在读取JSON时将JSON整数值转换为Date,并在将对象转换为JSON时将Date转换为整数值。

您可以通过采用并实现TransformType协议中的方法轻松创建自己的自定义转换。

public protocol TransformType {
    associatedtype Object
    associatedtype JSON

    func transformFromJSON(_ value: Any?) -> Object?
    func transformToJSON(_ value: Object?) -> JSON?
}

TransformOf

在许多情况下,您可以使用内置的转换类TransformOf快速执行所需的转换。TransformOf使用两个类型和两个闭包进行初始化。类型定义转换将转换为何种类型以及从何种类型转换,闭包执行实际的转换。

例如,如果您想将JSON String值转换为Int,您可以使用TransformOf如下所示

let transform = TransformOf<Int, String>(fromJSON: { (value: String?) -> Int? in 
    // transform value from String? to Int?
    return Int(value!)
}, toJSON: { (value: Int?) -> String? in
    // transform value from Int? to String?
    if let value = value {
        return String(value)
    }
    return nil
})

id <- (map["id"], transform)

以下是上述代码的更简洁版本

id <- (map["id"], TransformOf<Int, String>(fromJSON: { Int($0!) }, toJSON: { $0.map { String($0) } }))

子类

实现Mappable协议的类可以轻松地进行子类化。当子类化可映射类时,请遵循以下结构

class Base: Mappable {
    var base: String?
    
    required init?(map: Map) {

    }

    func mapping(map: Map) {
        base <- map["base"]
    }
}

class Subclass: Base {
    var sub: String?

    required init?(map: Map) {
        super.init(map)
    }

    override func mapping(map: Map) {
        super.mapping(map)
        
        sub <- map["sub"]
    }
}

请确保您的子类实现调用了正确的初始化程序和映射函数,以便也能应用超类中的映射。

泛型对象

ObjectMapper可以处理带有泛型类型的类,只要泛型类型也符合Mappable接口。请参见下面的示例。

class Result<T: Mappable>: Mappable {
    var result: T?

    required init?(map: Map){

    }

    func mapping(map: Map) {
        result <- map["result"]
    }
}

let result = Mapper<Result<User>>().map(JSON)

映射上下文

在映射过程中传递的Map对象,包含一个可选的MapContext对象,供开发人员使用,以便在映射过程中传递信息。

为了利用此功能,只需创建一个实现了MapContext(这是一个空的协议)的对象,并在初始化过程中将其传递给Mapper

struct Context: MapContext {
	var importantMappingInfo = "Info that I need during mapping"
}

class User: Mappable {
	var name: String?
	
	required init?(map: Map){
	
	}
	
	func mapping(map: Map){
		if let context = map.context as? Context {
			// use context to make decisions about mapping
		}
	}
}

let context = Context()
let user = Mapper<User>(context: context).map(JSONString)

ObjectMapper + Alamofire

如果您使用Alamofire进行网络连接,并希望将响应转换为Swift对象,则可以采用AlamofireObjectMapper。它是一个简单的Alamofire扩展,使用ObjectMapper自动将JSON响应数据映射到Swift对象。

ObjectMapper + Realm

ObjectMapper和Realm可以一起使用。只需遵循以下类结构,您就可以使用ObjectMapper生成您的Realm模型。

class Model: Object, Mappable {
    dynamic var name = ""

    required convenience init?(map: Map) {
        self.init()
    }

    func mapping(map: Map) {
        name <- map["name"]
    }
}

如果您想序列化关联的RealmObjects,可以使用ObjectMapper+Realm。它是一个简单的Realm扩展,将任意JSON序列化到Realm的List类中。

要序列化Swift的StringIntDoubleBool数组,您可以使用ObjectMapperAdditions/Realm。它会将Swift类型包装成可以在Realm的List类中存储的RealmValues。

注意:生成Realm对象的JSON字符串只能使用ObjectMappers的toJSON函数在Realm写入事务中进行。这是因为ObjectMapper使用映射函数中的(<-)标志,这些标志既用于序列化又用于反序列化。Realm检测到该标志并强制在写入块中调用toJSON函数,即使对象未被修改。

使用ObjectMapper的项目

如果您有一个使用、扩展或提供ObjectMapper工具的项目,请在此README部分的相应部分提交PR并提供您项目的链接。

待办事项

  • 改进错误处理。或许可以尝试使用throws
  • 类簇文档

贡献

欢迎贡献力量👍😃.

在提交任何pull请求之前,请确保已运行所包含的测试,并且它们已通过。如果您包括新功能,请为此编写测试用例。

安装

Cocoapods

使用CocoaPods 0.36或更高版本将ObjectMapper添加到您的项目中,方法是在您的Podfile中添加以下行

pod 'ObjectMapper', '~> 3.5' (check releases to make sure this is the latest version)

Carthage

如果您使用的是 Carthage,可以通过将 ObjectMapper 添加到您的 Cartfile 来添加依赖

github "tristanhimmelman/ObjectMapper" ~> 3.5 (check releases to make sure this is the latest version)

Swift 包管理器

要向基于 Swift 包管理器 的项目添加 ObjectMapper,请将其添加到您的 Package.swift 文件中的 dependencies 数组。

.package(url: "https://github.com/tristanhimmelman/ObjectMapper.git", .upToNextMajor(from: "4.1.0")),

在您的 Package.swift 文件中添加以下内容:

子模块

否则,ObjectMapper 可以作为一个子模块添加

  1. 通过打开终端,切换到您的顶级项目目录,并输入命令 git submodule add https://github.com/tristanhimmelman/ObjectMapper.git 来将 ObjectMapper 作为子模块添加。
  2. 打开 ObjectMapper 文件夹,并将 ObjectMapper.xcodeproj 拖放到您的应用项目文件导航器中。
  3. 在 Xcode 中,通过点击蓝色项目图标,然后在侧栏中“Targets”标题下选择应用程序目标来导航到目标配置窗口。
  4. 确保 ObjectMapper.framework 的部署目标与应用程序目标的部署目标匹配。
  5. 在窗口顶部的标签栏中,打开“Build Phases”面板。
  6. 展开“Target Dependencies”组,并添加 ObjectMapper.framework
  7. 点击面板左上角的 + 按钮并选择“New Copy Files Phase”。将此新阶段重命名为“Copy Frameworks”,将“Destination”设置为“Frameworks”,并添加 ObjectMapper.framework