序列化是将对象或数据结构转换为适合通过网络传输或保存到文件的序列格式的过程。相反的过程,反序列化,将数据恢复到原始状态。根据MDN Web Docs,序列化对任何进程间通信都是必不可少的。序列化是REST API、缓存和应用程序组件之间数据交换的基础。
要点
序列化是将内存中的对象转换为线性字节或字符序列的过程,可以通过网络传输、保存到文件或传递给另一个进程。没有序列化,网络通信、状态存储和进程间通信是不可能的。
序列化包括两个相反的过程。直接过程(序列化)将数据打包成传输格式。相反过程(反序列化)将数据恢复为对象。反序列化对安全性至关重要:不正确的输入数据可能导致应用程序中的漏洞。
在移动应用开发中,序列化无处不在:向服务器发送请求和处理响应、屏幕旋转时保存应用状态、在磁盘上缓存数据以及通过Intent(Android)或Segue(iOS)在屏幕间传输数据。
序列化格式分为文本和二进制。文本格式(JSON、XML)可以被人类阅读,不需要工具来查看。二进制格式(Protobuf、FlatBuffers、MessagePack)更紧凑、更快,但不经过反序列化无法阅读。选择格式是性能和调试便利性之间的折衷。
除了JSON、XML和Protobuf外,还有专门的格式:Google的FlatBuffers适合游戏和AR、MessagePack——JSON的紧凑二进制替代品、Apache的Avro适合Kafka中的大数据、YAML——支持注释的配置格式。
| 格式 | 类型 | 模式 | 大小 | 速度 |
|---|---|---|---|---|
| JSON | 文本 | 可选 | 中等 | 中等 |
| XML | 文本 | XSD | 大 | 低 |
| Protobuf | 二进制 | 必须 | 小 | 高 |
| FlatBuffers | 二进制 | 必须 | 小 | 最高 |
| MessagePack | 二进制 | 否 | 小 | 高 |
| Avro | 二进制 | JSON Schema | 小 | 高 |
序列化在移动平台上有其特定:流量有限、处理器较弱以及屏幕旋转时保存状态的必要性。在Android上使用Gson、Moshi、Kotlinx Serialization。在iOS上使用Codable、JSONSerialization、PropertyListEncoder。正确选择库会对应用程序的性能产生关键影响。
Kotlinx Serialization——JetBrains为Kotlin Multiplatform Mobile打造的现代库。它支持JSON、Protobuf、CBOR和自定义格式。代码生成通过Kotlin Serialization插件在编译阶段完成,无需反射即可确保高性能。
Kotlinx Serialization库使用@Serializable注解用于类,并使用编译器插件生成序列化器。这确保了高性能和类型安全。默认格式是JSON,但其他格式也可通过额外模块支持。
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlinx.serialization.encodeToString
import kotlinx.serialization.decodeFromString
@Serializable
data class Project(
val id: Int,
val name: String,
val platforms: List<String>,
val active: Boolean
)
val json = Json {
prettyPrint = true
ignoreUnknownKeys = true
encodeDefaults = true
}
fun main() {
val project = Project(1, "MobileApp",
listOf("Android", "iOS"), true)
// 序列化
val jsonString = json.encodeToString(project)
// 反序列化
val restored = json.decodeFromString<Project>(jsonString)
}
Codable协议——Swift中内置的序列化机制。它结合了Encodable(序列化)和Decodable(反序列化)协议。JSONEncoder和JSONDecoder自动处理嵌套结构、数组、可选值和通过CodingKeys的自定义键。
import Foundation
struct AppConfig: Codable {
let appName: String
let version: String
let features: [String]
let isProduction: Bool
}
let config = AppConfig(
appName: "MyApp",
version: "2.1.0",
features: ["push", "analytics", "offline"],
isProduction: true
)
let encoder = JSONEncoder()
encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
guard let data = try? encoder.encode(config) else { return }
let jsonString = String(data: data, encoding: .utf8)
性能序列化格式根据三个指标评估:消息大小、序列化速度和反序列化速度。对于移动应用,这三者都很重要:大小影响流量和加载时间,速度影响界面响应性和应用启动时间。
Protobuf和FlatBuffers凭借二进制表示展示了最佳结果。FlatBuffers的不同之处在于它不需要单独的反序列化步骤——数据直接从二进制缓冲区读取,这对于要求最低延迟的游戏和AR应用非常理想。尽管性能较低,JSON凭借其简单性和普遍性仍然是REST API最流行的格式。
| 场景 | 推荐格式 | 原因 |
|---|---|---|
| REST API | JSON | 普遍性、可读性、支持 |
| 微服务 | Protobuf | 紧凑性、速度、gRPC |
| 游戏 / AR | FlatBuffers | Zero-copy、最低延迟 |
| 大数据 | Avro | 与Kafka和Hadoop兼容 |
| 配置 | YAML | 注释、可读性 |
| Android布局 | XML | 平台标准 |
在1000个用户对象集上的实践测试显示:Protobuf创建12KB的消息(JSON——85KB,XML——120KB)。序列化时间:Protobuf——2毫秒,JSON——8毫秒,XML——25毫秒。这些数据使得二进制格式成为高负载系统和流量有限的移动应用的首选。
示例展示了同一对象在不同格式中的序列化。这有助于直观地比较大小和可读性。同一个User对象将在JSON、XML和Protobuf中序列化——可以清楚地看到JSON比XML更紧凑,而Protobuf是最紧凑的,同时不可读。
JSON——最简主义语法,键在引号中,值为不同类型。占用80个字符。可读性高,视觉结构清晰。适合开发和调试速度重要的API。
XML——每个元素裹在开始和结束标签中。占用150个字符。可读性中等,结构严格。适合文档流程和需要通过XSD验证的系统。
Protobuf——二进制,这些数据占用32字节。不可读——需要反序列化才能查看。最小大小使其成为高负载系统和移动应用的理想选择。
{
"id": 42,
"name": "IT Sectr",
"email": "team@itsectr.com",
"role": "admin",
"active": true
}
<user>
<id>42</id>
<name>IT Sectr</name>
<email>team@itsectr.com</email>
<role>admin</role>
<active>true</active>
</user>
最佳实践帮助避免典型错误并为项目选择正确的序列化策略。遵循这些建议可以提高性能、安全性和代码可维护性。
序列化安全——至关重要的方面,尤其是从不可信来源反序列化数据时。反序列化攻击可能导致远程代码执行(RCE),这是网络和移动应用中最危险的漏洞之一。最著名的案例与Java Serializable和Python pickle有关。
Protobuf和JSON具有内置的防护机制,因为它们只处理数据,而不是任意对象。相反,Java Serializable可以恢复classpath中可用的任何类,这使得它在接受来自外部来源的数据时具有危险性。在Android上,建议使用Kotlinx Serialization或Moshi代替标准的Java Serialization。
额外的安全措施:为输入数据大小设置限制,在反序列化前验证模式,不要相信HTTP头中的Content-Type,为允许的类使用白名单。定期更新序列化库,因为其中的漏洞会定期被发现和修复。
常见问题
序列化将对象转换为字节序列,编组(marshalling)在保持类型和结构的同时在不同地址空间之间传输数据。编组包括序列化作为过程的一部分,但也可能包括引用编码和内存管理。
Google的FlatBuffers凭借零拷贝反序列化提供了最高速度——数据直接从二进制缓冲区读取,无需转换。Protobuf位居第二,JSON第三。XML是常见格式中最慢的。
Kotlinx Serialization是Kotlin新项目的最佳选择:编译器生成、支持Kotlin Multiplatform、空安全。Moshi适合Java项目,性能优于Gson。Gson是最简单的入门库,但较慢且使用反射。
循环引用会导致序列化时无限递归。解决方案:使用ID引用代替对对象的直接引用,应用特殊的序列化适配器(例如Jackson中的@JsonIgnore),或重新设计数据模型以消除循环。
会,尤其是从不可信来源反序列化数据。反序列化漏洞可能导致远程代码执行。建议:不要从不可信来源反序列化数据,在反序列化时使用类白名单,并在处理前验证数据模式。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。