序列化和反序列化是将对象转换为可传输或存储格式的基本过程。在移动开发中,这些机制在每个网络请求、状态保存和进程间通信中都有使用。根据MDN Web Docs,2024,JSON仍然是最流行的序列化格式,在Web和移动应用中都超过了XML和Protocol Buffers。
要点
序列化是将应用程序对象转换为适合通过网络发送或存储在磁盘上的格式的过程。反序列化执行反向转换,从接收到的数据中恢复对象。在移动开发中,序列化应用于API请求、屏幕状态保存、缓存以及通过Intent或Bundle进行数据传输。
应用组件之间或应用与服务器之间的每次交互都需要序列化。REST API以JSON或XML格式传输数据,gRPC使用Protocol Buffers,Android上的进程内通信使用Parcelable。没有序列化,就不可能将复杂对象传递到进程边界之外或将其保存到数据库中。
// 序列化的一般原则
data class User(
val id: Int,
val name: String,
val email: String
)
// 序列化:对象 -> JSON
fun serializeUser(user: User): String {
return """{"id":${user.id},"name":"${user.name}","email":"${user.email}"}"""
}
// 反序列化:JSON -> 对象
fun deserializeUser(json: String): User? {
// 将JSON解析为对象
return Gson().fromJson(json, User::class.java)
}
| 格式 | 大小 | 速度 | 可读性 | 类型化 |
|---|---|---|---|---|
| JSON | 中等 | 高 | 高 | 动态 |
| XML | 大 | 中等 | 高 | XSD模式 |
| Protocol Buffers | 小 | 非常高 | 低 | 严格.proto |
| FlatBuffers | 小 | 最高 | 低 | 严格.fbs |
JSON(JavaScript Object Notation)是一种基于JavaScript对象语法的轻量级文本格式。JSON支持字符串、数字、布尔值、数组和嵌套对象,覆盖了移动应用中大多数数据传输场景。该格式独立于平台:每个生态系统都提供内置的解析工具。
JSON因其简单性和通用性而胜出。开发者不需要模式即可进行基本使用 — 结构在解析过程中动态确定。格式的可读性简化了调试和测试:服务器的响应可以在任何开发者工具中查看。在两个移动平台上,JSON都可以原生处理,无需连接第三方库。
// Codable — Swift中的原生JSON序列化
struct User: Codable {
let id: Int
let name: String
let email: String
}
let jsonString = """{"id":1,"name":"John","email":"john@test.com"}"""
let jsonData = Data(jsonString.utf8)
let decoder = JSONDecoder()
let user = try! decoder.decode(User.self, from: jsonData)
Protocol Buffers(protobuf)是谷歌为高性能系统开发的二进制序列化格式。与JSON不同,protobuf需要在.proto文件中预先定义模式,但提供显著更小的数据量和更高的处理速度。该格式用于gRPC、Firebase Firestore和谷歌的内部服务。
protobuf模式描述带有类型化字段的消息,每个字段都有一个唯一的编号。protoc编译器在目标语言中生成类,这些类自动执行序列化和反序列化。Protobuf通过添加和删除字段的规则支持模式的演进,而不会失去向后兼容性。
// 在user.proto中定义模式
syntax = "proto3";
message User {
int32 id = 1;
string name = 2;
string email = 3;
repeated string roles = 4;
}
// 在Kotlin中生成的代码
val user = UserProto.User.newBuilder()
.setId(1)
.setName("John")
.setEmail("john@test.com")
.build()
val bytes: ByteArray = user.toByteArray()
XML(可扩展标记语言)是一种具有严格结构、支持命名空间和通过XSD模式进行验证的格式。XML在紧凑性和解析速度方面不如JSON,但在Android开发中仍然很受欢迎,用于布局文件、AndroidManifest、资源和Gradle配置。在iOS上,XML用于plist文件和某些旧版服务。
Android提供了三种解析XML的方式:DOM(将整个文档加载到内存中)、SAX(事件驱动的流式解析)和XmlPullParser(混合方法)。XmlPullParser是移动设备的推荐选择,因为它可以有效利用有限的内存,并允许在读取时处理文档。
// Android上的XmlPullParser
fun parseUserXml(inputStream: InputStream): User? {
val parser = Xml.newPullParser()
parser.setInput(inputStream, Xml.Encoding.UTF_8.name)
var id: Int? = null
var name: String? = null
var email: String? = null
while (parser.next() != XmlPullParser.END_DOCUMENT) {
if (parser.eventType == XmlPullParser.START_TAG
&& parser.name == "email") {
email = parser.nextText()
}
}
return User(id!!, name!!, email!!)
}
每个平台都提供自己的序列化工具。在iOS上,主要机制是Codable协议以及JSONEncoder和JSONDecoder,对于Objective-C则是NSJSONSerialization。Android使用Gson、Moshi和kotlinx.serialization库。对于进程间的数据传输,Android使用Parcelable,iOS使用NSKeyedArchiver。Kotlin项目越来越常选择kotlinx.serialization — JetBrains的解决方案,它支持多平台且不依赖于反射,通过Kotlin编译器插件在编译阶段生成序列化器。
Android支持两种用于Intent和Bundle的序列化机制。Serializable是使用反射的标准Java机制,导致性能低下。Parcelable是Android特有的协议,需要手动实现writeToParcel和createFromParcel方法,但由于直接操作字节,速度快了数十倍。
// Android上的Parcelable
@Parcelize
data class UserParcel(
val id: Int,
val name: String,
val email: String
) : Parcelable
// 通过Intent传输
val intent = Intent(this, DetailActivity::class.java)
intent.putExtra("user", userParcel)
startActivity(intent)
在iOS上,控制器之间的数据传输使用Codable预先序列化为Data,而进程间通信则使用NSKeyedArchiver将对象转换为二进制Property List格式。SwiftUI中的现代项目更倾向于通过JSONEncoder对数据进行编码,以便在应用程序模块之间传输。
不可信数据的错误反序列化可能导致漏洞。通过序列化的攻击利用了Java中被覆盖的readObject方法或第三方库的不安全反序列化器。在Android上,应避免对来自不可信来源的数据使用Serializable,而应使用Parcelable或在反序列化后手动验证所有字段。在iOS上,JSONDecoder默认对类型严格要求,但在使用JSONSerialization时,开发者必须通过条件转换检查类型。
选择序列化格式时,需要考虑性能要求、数据大小和兼容性。对于REST API和微服务架构,最佳选择仍然是JSON — 它被所有平台和语言支持。对于高负载系统和流量受限的移动应用,更推荐Protocol Buffers,它提供更小的体积和更快的反序列化。XML仅适用于配置场景和与旧版系统的集成。在现代项目中,FlatBuffers也越来越受欢迎 — 这是一种无需反序列化阶段的二进制格式,用于游戏引擎和延迟极低的应用。每种格式都有自己的定位,正确的选择直接影响应用程序的运行速度和消耗的流量。
常见问题
序列化是将应用对象打包成可以通过网络发送或保存到文件的格式。想象你给一个物品拍照 — 照片就是序列化,而从照片中恢复物品就是反序列化。
JSON是一种人类可读的文本格式,不需要模式。Protocol Buffers是一种带有必需模式(.proto)的二进制格式,体积更小、速度更快。Protobuf用于高负载系统,JSON用于通用兼容性。
XML仍然是配置文件(AndroidManifest、布局资源)、复杂嵌套文档和通过XSD进行严格验证的系统的标准。XML也应用于需要命名空间支持的SOAP协议和旧版系统。
Codable是一个Swift协议,结合了Encodable和Decodable,用于自动序列化和反序列化。编译器为结构或类的所有属性生成encode(to:)和init(from:)方法的实现。
Parcelable比Serializable快得多,因为它不使用反射并直接将字段写入字节流。在现代设备上,差异可以达到Parcelable快10倍的水平。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。