Bun v1.4 中新增
- 使用
Bun.XML.parse和Bun.XML.stringify解析和序列化 XML - 在运行时将 XML 文件
import和require为模块(包括热重载和 watch 模式支持) - 在使用 Bun 打包器的前端应用中
import和requireXML 文件
运行时 API
Bun.XML.parse()
将 XML 文档解析为普通 JavaScript 对象。
- 结果只有一个键,即根元素的名称
- 没有属性且没有子元素的元素会变成其文本内容,并去除周围的空白(为空时为
"") - 其他元素会变成一个对象:每个属性对应一个
"@name"键,每个不同的子元素名称对应一个键——当该名称重复时使用数组,并保持文档顺序——如果存在经过修剪的字符数据,则使用"#text"表示 - CDATA 节和实体引用已经展开到文本中。注释和处理指令会被丢弃
- 所有值都是字符串。不会将任何内容强制转换为数字、布尔值或
null
{ compact: false },以获得一个能够保留所有文档顺序的根元素节点树:
{ name, attributes, children };children 保存子元素和字符串,文本会原样传递(包括元素之间仅由空白组成的文本)。
输入类型和编码
XML.parse 接受字符串,或作为 Buffer、TypedArray、ArrayBuffer 或 Blob 的字节数据。
字符串已经是解码后的文本,因此会检查其中 encoding 声明的语法,但除此之外会忽略该声明。字节数据会按照 XML 规则解码:字节顺序标记或 <?xml version="1.0" encoding="..."?> 中的 encoding 会选择 UTF-8(默认)、UTF-16(任一字节序)或 ISO-8859-1。其他编码会抛出错误。
错误处理
当文档格式不正确时,Bun.XML.parse() 会抛出 SyntaxError:
Bun.XML.stringify()
将任一形状重新序列化为 XML。输出没有 XML 声明,并且始终格式正确:会对 &、<、>(以及属性中的引号、制表符和换行符)进行转义,而不是 XML 名称的元素名或属性名会抛出错误。
name 以及 children 或 attributes 属性的值会作为节点写入;其他内容都是紧凑对象,并且必须恰好有一个用于命名根元素的键。字符串、数字、布尔值、bigint 和 Date(作为 ISO 字符串)会变成文本,null 会变成空元素,而 undefined、函数和符号会像 JSON.stringify 跳过它们一样被跳过(与 JSON.stringify 不同,bigint 会以十进制数字写入,而不是被拒绝)。
美化打印
传入space 参数(空格数量或缩进字符串,与 JSON.stringify 一致)即可缩进仅包含元素的内容。包含文本的元素会以内联形式写入,因此字符数据不会改变:
XML.parse 生成的任何值,无论哪种形状,XML.parse(XML.stringify(value)) 都会返回 value。
模块导入
ES Modules
你可以直接导入 XML 文件。文件的解码方式与传递给XML.parse 的字节数据相同(根据字节顺序标记或声明使用 UTF-8、UTF-16 或 ISO-8859-1),模块的值是上文所述的紧凑对象:
config.xml
默认导入
app.ts
命名导入
根元素也可以作为命名导入使用:app.ts
CommonJS
app.ts
导入属性
使用with { type: "xml" } 将其他扩展名的文件作为 XML 解析:
XML 热重载
使用bun --hot 运行应用程序时,Bun 会在 XML 文件发生变化时重新加载它们:
server.ts
terminal
打包器集成
使用 Bun 进行打包时,导入的 XML 文件会在构建时解析,并以内联 JavaScript 对象的形式写入:terminal
- 生产环境中零运行时 XML 解析开销
- 更小的打包体积
- 对未使用的属性进行 Tree Shaking
动态导入
XML 文件可以动态导入:一致性
Bun 的 XML 解析器使用 Rust 编写,实现了 XML 1.0(第五版),作为一种不读取外部实体的非验证处理器:- 整个文档(包括内部 DTD 子集)都必须格式正确——否则会抛出
SyntaxError - 文档中声明的内部实体会被展开(具有展开限制,因此“billion laughs”负载会失败,而不会耗尽内存),属性值会被规范化,并且会应用内部子集中声明的属性默认值
- 永远不会获取或读取外部 DTD 和外部实体,因此不存在 XXE 攻击面。在没有 DTD 的文档中,对未声明实体的引用会出错;当 DOCTYPE 指向外部子集(或使用可能声明该实体的参数实体)时,该引用会按原样保留(
保持为 ),除非文档声明了standalone="yes" - 不会根据 DTD 进行验证,不会解析命名空间(带前缀的名称会原样保留),并且会跳过注释和处理指令
性能
解析器分两个阶段工作,与 Bun 的 JSON 解析器类似:SIMD 阶段(运行时分派 AVX2/AVX-512/NEON/SVE 内核)会找出可能改变解析结果的字节,因此不会逐字节扫描字符数据、属性值、注释和 CDATA 节;元素名和属性名会复用 JavaScriptCore 的原子字符串缓存,其方式与JSON.parse 相同。
bench/xml/xml.mjs 会在相同文档上比较 Bun.XML.parse 与常用的 npm 解析器(越低越好;Linux x64,单核):