Parsing — 是对结构化数据进行语法分析的过程,从 JSON、XML、CSV 或 Protobuf 中提取信息供应用程序使用。根据 Square Engineering (2025) 的数据,解析策略的选择直接影响移动应用的性能。流式解析 可以在不将整个文档加载到内存的情况下处理兆字节的数据。
要点
解析(语法分析)— 是将字符序列转换为适合程序处理的结构化表示的过程。在移动开发的背景下,解析指的是分析服务器响应、配置文件、本地缓存和资源文件。每种格式都需要自己的方法:JSON — 轻量级且内置于 SDK 中,XML — 严格且带有 XSD 模式验证,CSV — 用于表格数据的最小化格式,Protobuf — 具有严格模式的二进制格式。
任何解析器,无论格式如何,都经历三个阶段。词法分析 将原始文本分解为词法单元:键、字符串、数字、分隔符和注释。语法分析 检查语法的正确性 — JSON 中的括号平衡、XML 中标签的正确嵌套。输出结构的构建 形成对象表示:JSONObject、Map 或用于进一步处理的令牌流。根据 Apple (2026) 的数据,iOS 上的内置 Foundation 解析器使用惰性解析 — 仅验证 JSON 的开头以节省资源。
手动解析(JSONSerialization、XmlPullParser)可以完全控制过程,适用于非标准格式或需要对每个字段进行自定义验证的情况。自动库(JSONDecoder、Moshi、kotlinx.serialization)加速开发,但隐藏了错误处理的细节。选择取决于需求:如果数据结构是动态的或事先未知 — 手动解析更可取。如果模式是固定的 — 自动反序列化可将代码减少 60-70%。
解析 — 更广泛的操作:可以流式执行,不一定要创建类型化对象。反序列化 — 解析的特例,其结果始终是预先已知类的实例,具有特定类型的属性。JSON 解析器将文档解析为 Map 或 JsonElement,而反序列化器将相同的 JSON 转换为具有已知字段和类型的特定 User、Order 或 Product 对象。
| 特性 | 解析 | 反序列化 |
|---|---|---|
| 结果 | JsonElement、Map、令牌流 | 类型化对象(User、Order) |
| 流式模式 | 是(SAX、JsonReader) | 否(整个对象在内存中) |
| 类型化 | 可选,动态类型 | 必需,静态类型 |
| 示例 | XmlPullParser.next()、SAX events | json.decodeFromString<User>() |
流式解析 — 解析相对于反序列化的主要优势。Android 上的 XmlPullParser 在读取时解析 XML,无需将整个文档加载到 RAM 中。这对于大型 API 响应、包含数百个元素的 RSS 订阅解析或处理来自服务器转储的数千个 JSON 对象数组至关重要。流式解析器逐个读取令牌,消耗恒定量的内存,与文档大小无关。
JSON — 主导的数据交换格式。在 iOS 上,内置的 JSONSerialization 将 JSON 解析为 Any,而 JSONDecoder 直接解析为 Codable 结构。在 Android 上,标准的 org.json 提供 JSONObject 和 JSONArray 用于手动解析。Gson 和 Moshi 添加了自动转换。手动和自动方法之间的选择是网络层的第一个架构决策。
val jsonString = """{"users":[{"id":1,"name":"Alice"}]}"""
val jsonObject = JSONObject(jsonString)
val usersArray = jsonObject.getJSONArray("users")
for (i in 0 until usersArray.length()) {
val item = usersArray.getJSONObject(i)
val id = item.getInt("id")
val name = item.getString("name")
// 用户处理
}
通过 org.json 手动解析 在响应结构事先未知或变化很大时很有用。这种方法可以完全控制错误处理和可空字段,但与自动反序列化相比需要更多的样板代码。对于小型项目或原型来说这是可以接受的,但在生产环境中更推荐 Moshi 或 kotlinx.serialization。
JsonReader 来自 Moshi 库 — 是流式解析 JSON 的强大工具。它逐个令牌读取文档,使用 beginObject、nextName、nextString、nextInt 和 endObject 方法。这种方法允许处理任何大小的文档,内存消耗恒定。根据 Google (2026) 的数据,通过 JsonReader 进行流式解析比将整个文档加载到 JsonObject 中快 2-3 倍,内存消耗减少高达 80%。
val reader = JsonReader(StringReader(json))
reader.beginObject()
while (reader.hasNext()) {
val name = reader.nextName()
when (name) {
"id" -> val id = reader.nextInt()
"name" -> val name = reader.nextString()
"email" -> val email = reader.nextString()
else -> reader.skipValue()
}
}
reader.endObject()
示例展示了 JsonReader 流式解析 JSON。每次调用 nextName() 和 nextInt() 都会将光标前进到下一个令牌。skipValue() 跳过不需要的字段而不解析它们 — 这在 JSON 包含 50 个字段但只需要 3 个时特别有用。结果是 — 最小的内存消耗和最大的速度。
XML 继续在 Android 项目中用于资源和企业应用程序的 SOAP 服务。在 Android 上,内置的 XmlPullParser 提供流式解析,内存消耗最小 — 它不加载 DOM 树,而是生成 START_TAG、TEXT、END_TAG 事件。iOS 使用 XMLParser 采用委托方法:parser(didStartElement:) 和 parser(foundCharacters:) 方法顺序处理元素。
CSV — 用于导出和导入表格数据的最简单格式。在移动应用中,CSV 用于导出报告、加载参考手册(国家、货币、代码列表)和同步离线数据。流式库适用于 CSV 解析:Apache Commons CSV 和 OpenCSV 逐行处理文件,转义值内的引号和逗号。在 iOS 上,CSV 解析可以通过分隔符 NSString.components(separatedBy:) 实现,并处理边缘情况。
class CSVParser: NSObject, XMLParserDelegate {
private var currentItem: [String: String] = [:]
private var currentElement: String = ""
func parser(
_ parser: XMLParser,
didStartElement elementName: String,
namespaceURI: String?,
qualifiedName qName: String?,
attributes attributeDict: [String: String]
) {
currentElement = elementName
if elementName == "item" {
currentItem = [:]
}
}
func parser(_ parser: XMLParser,
foundCharacters string: String) {
currentItem[currentElement] = string
}
}
iOS 上 XMLParser 委托解析 RSS 订阅的示例。didStartElement 方法在每个开始标签时调用,foundCharacters 返回标签之间的文本内容,didEndElement 完成元素。这种事件驱动的架构允许处理任何大小的 XML,无需将 DOM 树加载到内存中 — 这对资源受限的移动设备至关重要。
在 Android 上,CSV 解析器的选择取决于数据量。OpenCSV 支持流式 API、用于映射到 POJO 的注解和自定义分隔符。Apache Commons CSV — 更低级别,但在大文件上更快。对于简单情况(1-2 列),通过 String.split(",") 手动解析并处理转义就足够了。对于具有大型 CSV 文件的生产项目,建议使用 OpenCSV。
Protocol Buffers 来自 Google — 二进制序列化格式,在速度和紧凑性方面优于 JSON。Protobuf 消息占用的空间比等效 JSON 少 3-6 倍,由于二进制格式和严格模式,解析速度快 4-10 倍。主要缺点:缺乏人类可读性和需要通过 protoc 编译器从 .proto 文件生成代码。
Protobuf 流式解析 通过 CodedInputStream 实现 — 客户端在消息到达时逐个读取字段,无需加载整个消息。每个字段都有 tag(编号 + 类型),解析器可以跳过未知字段。根据 Google (2026) 的数据,流式 Protobuf 解析在处理 1 MB 及以上的消息时可减少高达 90% 的内存消耗,这对于 RAM 有限的移动设备至关重要。Protobuf-lite — 专为 Android 优化的版本,在生成的代码大小方面进行了优化。
常见问题
JSONSerialization — 手动解析,返回 Any(Dictionary、Array)无需类型化。JSONDecoder — 自动反序列化为 Codable 类型。JSONSerialisation 为动态结构提供灵活性,JSONDecoder — 为已知模式提供速度和类型安全。在生产环境中建议使用 JSONDecoder。
流式解析在以下情况下是 必需的:响应超过 500 KB、解析 RSS/Atom 订阅以及在内存有限的设备上处理文件。对于不超过 100 KB 的典型 API 响应,通过 JSONDecoder 或 Moshi 进行自动反序列化更可取 — 代码更简洁,开发更快。
Android SDK 包含 org.json(JSONObject、JSONArray)、XmlPullParser(XML)和 android.util.JsonReader。对于 CSV、YAML 和 Protobuf,需要额外的库:OpenCSV 用于 CSV、SnakeYAML 用于 YAML、protobuf-javalite 用于 Protobuf。
在 kotlinx.serialization 中使用 lenient 模式,或在 Moshi 中使用 JsonReader.setLenient(true) — 它容忍多余的逗号、未转义的引号和单行注释。要进行完整验证,请通过 Pact 或 Spring Cloud Contract 实施契约测试。
SAX(Simple API for XML)— 流式 XML 解析器,在遍历文档时生成 startElement、characters、endElement 事件。通过 Android 上的 XmlPullParser 和 iOS 上的 XMLParser 实现。用于 RSS 阅读器、离线数据同步和 SVG 文件处理。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。