在Node.js服务端开发中,经常需要把业务层产生的普通JS对象转换成XML格式,例如调用传统SOAP接口、生成RSS源或导出配置文件。xml2js这个库通常被用来解析XML,但它自带的Builder模块同样可以完成反向操作,将对象树序列化为合法的XML文本。理解Builder的工作方式和可选参数,能让我们少写很多字符串拼接代码,也避免手工处理特殊字符转义带来的隐患。

一、安装与基本用法
首先通过npm安装xml2js依赖。安装完成后,从包中引入Builder类即可开始使用。Builder的构造函数可以接收一个配置对象,但在最简单的情况下也可以不传任何参数,直接调用buildObject方法并传入JS对象。
下面的示例展示了如何把一个包含用户信息的对象转成XML。注意对象里的键名会直接变成XML的标签名,嵌套对象会变成子元素,数组则会被展开为多个同名节点。
const { Builder } = require('xml2js');
const obj = {
user: {
name: '张三',
age: 28,
hobbies: ['阅读', '游泳']
}
};
const builder = new Builder();
const xml = builder.buildObject(obj);
console.log(xml);
上述代码运行后会输出带XML声明头的字符串,结构为根节点user包裹name、age与多个hobby子节点。这种方式适合快速生成简单报文,但如果对格式有严格要求,就需要进一步设置Builder参数。
二、控制输出格式与声明头
默认情况下,Builder会在开头插入XML声明,并使用两空格缩进。我们可以通过xmldec控制声明内容,用renderOpts调整缩进与换行行为。比如在一些老系统对接时,对方要求不带声明或指定encoding,这时显式配置比事后裁剪字符串更稳妥。
下面的配置演示了如何去掉声明、关闭缩进以压缩体积,以及自定义根节点名称。当传入的obj本身没有单一根时,可以用rootName参数包裹。
const { Builder } = require('xml2js');
const obj = {
item: { id: 1, title: '测试' }
};
const builder = new Builder({
xmldec: { version: '1.0', encoding: 'UTF-8', standalone: null },
renderOpts: { pretty: false, indent: '', newline: '' },
rootName: 'root'
});
const xml = builder.buildObject(obj);
console.log(xml);
关闭pretty后输出会变为单行,适合网络传输。而rootName确保最外层始终有一个固定标签,避免对象存在多个顶层键时产生非法XML。实际项目中建议把这些配置抽成公共函数,统一团队输出风格。
三、自定义属性与文本字段
xml2js解析时默认用$表示属性、_表示文本,Builder也沿用这套约定。也就是说,如果想生成标签属性,需要在对象里用$键放置属性对象;想设置标签内容则用_键。这种约定在双向转换时能保持对称,但初次使用容易困惑。
我们可以通过attrkey和charkey选项修改这两个特殊键名,防止和业务数据里的下划线字段撞名。以下示例把属性键改为attributes,文本键改为content,并生成一个带属性和文本的product节点。
const { Builder } = require('xml2js');
const obj = {
product: {
attributes: { sku: 'A100', stock: 5 },
content: '高性能鼠标'
}
};
const builder = new Builder({
attrkey: 'attributes',
charkey: 'content'
});
const xml = builder.buildObject(obj);
console.log(xml);
输出结果中product标签会带有sku与stock属性,标签体则为“高性能鼠标”。当对象来自外部接口且字段不可控时,提前规划好attrkey和charkey能降低映射复杂度。同时要注意,属性值会被自动转义,不必自己替换引号与尖括号。
四、处理数组与命名空间
在生成复杂报文时,同一级出现多个重复子节点是非常常见的需求,JS数组天然对应这种结构。如果某个标签需要带命名空间前缀,可直接在键名写前缀,或在属性里声明xmlns。Builder不会自动管理命名空间前缀映射,需要开发者自己保证声明完整。
下面例子展示订单中包含多个order_item,并为根节点声明命名空间。这样生成的XML更容易通过严格校验的解析器。
const { Builder } = require('xml2js');
const obj = {
'ns:order': {
attributes: { 'xmlns:ns': 'http://ipipp.com/order' },
order_item: [
{ id: 1, num: 2 },
{ id: 2, num: 1 }
]
}
};
const builder = new Builder();
const xml = builder.buildObject(obj);
console.log(xml);
数组order_item被展开为两个并列节点,命名空间前缀ns出现在根标签及其声明中。若对接方要求特定前缀,只要对象键名与声明保持一致即可。遇到深层嵌套时,建议先用普通JS对象构图,再通过Builder统一序列化,而不是边拼边写XML字符串。
五、常见误区与排错
一个常见错误是以为Builder会自动把Date、Buffer等类型转成字符串。实际上它只会做基本的JSON式序列化,Date对象可能输出为ISO字符串,而Buffer会转成JSON结构,导致XML非法。因此在传入前应先把字段映射成字符串或数字。
另一个误区是忽略特殊字符。虽然Builder会转义标签文本,但如果手动拼接到属性里就可能漏掉。统一走对象配置就能规避。以下代码演示了日期预处理与错误对照。
const { Builder } = require('xml2js');
const raw = {
event: {
time: new Date(),
desc: '正常<演示>'
}
};
// 错误:直接传入Date
// const bad = new Builder().buildObject(raw);
// 正确:先格式化
const safe = {
event: {
time: raw.event.time.toISOString(),
desc: raw.event.desc
}
};
const xml = new Builder().buildObject(safe);
console.log(xml);
经过预处理的对象能稳定输出,特殊符号也被转义。总结来说,使用xml2js的Builder把JS对象转XML,核心就是规划好对象结构、特殊键名与输出参数,其余转义与格式化工作交给库本身,既省力又不易出错。
Node.jsxml2jsXML_Builder修改时间:2026-08-03 05:48:30