如何按link字段语言后缀拆分Google供应商标准XML并保留原有结构
问题描述
我有一份符合Google供应商标准的XML文件,包含波兰语和英语两种语言的商品数据,需要拆分为两个独立的XML文件,且完整保留原文件结构。目前的难点是语言标识仅出现在link字段中,以.pl或.en后缀区分。
优先希望能直接生成两个独立文件,仅生成单文件也可以,我可以更换筛选条件运行第二次得到另一个文件。
我原本想用JS实现类似如下逻辑(伪代码):
file.rss.channel.children.filter(item=>{return item.link.includes(".en")})
我尝试用xmlstarlet实现该需求,但没有成功。
输入文件示例
<rss xmlns:g="http://base.google.com/ns/1.0" xmlns:c="http://base.google.com/cns/1.0" version="2.0"><channel><title><![CDATA[ Brand ]]></title><link><![CDATA[ site.link.pl ]]></link><description><![CDATA[ ]]></description><item><g:id>132430</g:id><link><![CDATA[item.link.pl]]></link><g:canonical_link>item.link.pl</g:canonical_link></item><item><g:id>132431</g:id><link><![CDATA[item.link.en]]></link><g:canonical_link>item.link.en</g:canonical_link></item></channel></rss>
预期输出英文版本示例
<rss xmlns:g="http://base.google.com/ns/1.0" xmlns:c="http://base.google.com/cns/1.0" version="2.0"><channel><title><![CDATA[ Brand ]]></title><link><![CDATA[ site.link.pl ]]></link><description><![CDATA[ ]]></description><item><g:id>132431</g:id><link><![CDATA[item.link.en]]></link><g:canonical_link>item.link.en</g:canonical_link></item></channel></rss>
解决方案
方案1:xmlstarlet命令行实现
直接通过删除不符合条件的item节点即可输出目标文件,原XML的公共节点(title、channel根link、description)会完整保留:
# 生成英文版本文件 xmlstarlet ed -d '//item[not(ends-with(link/text(), ".en"))]' 输入文件名.xml > output_en.xml # 生成波兰语版本文件 xmlstarlet ed -d '//item[not(ends-with(link/text(), ".pl"))]' 输入文件名.xml > output_pl.xml
如果你使用的是旧版本xmlstarlet(仅支持XPath1.0,无ends-with方法),可以替换为contains写法:
# XPath1.0兼容版,生成英文文件 xmlstarlet ed -d '//item[not(contains(link/text(), ".en"))]' 输入文件名.xml > output_en.xml
方案2:Node.js实现(符合你原本的JS逻辑)
如果你更熟悉JS语法,可以用fast-xml-parser库处理,完美保留CDATA、命名空间等原结构:
- 安装依赖
npm install fast-xml-parser
- 新建处理脚本
split-xml.js
const { readFileSync, writeFileSync } = require('fs'); const { XMLParser, XMLBuilder } = require('fast-xml-parser'); // 解析&生成配置,保留所有原结构 const parser = new XMLParser({ ignoreAttributes: false, cdataPropName: '__cdata', preserveOrder: true }); const builder = new XMLBuilder({ ignoreAttributes: false, cdataPropName: '__cdata', preserveOrder: true, format: true }); // 读取原文件 const rawXml = readFileSync('输入文件名.xml', 'utf-8'); const xmlTree = parser.parse(rawXml); const channelNodes = xmlTree[0]['rss'][0]['channel']; const commonNodes = []; const enItems = []; const plItems = []; // 拆分公共节点和不同语言的商品项 for (const node of channelNodes) { const nodeName = Object.keys(node)[0]; if (nodeName === 'item') { const linkNode = node['item'].find(n => Object.keys(n)[0] === 'link')?.['link']?.[0]; const linkVal = linkNode.__cdata || linkNode['#text'] || ''; if (linkVal.endsWith('.en')) enItems.push(node); else if (linkVal.endsWith('.pl')) plItems.push(node); } else { commonNodes.push(node); } } // 输出英文文件 xmlTree[0]['rss'][0]['channel'] = [...commonNodes, ...enItems]; writeFileSync('output_en.xml', builder.build(xmlTree), 'utf-8'); // 输出波兰语文件 xmlTree[0]['rss'][0]['channel'] = [...commonNodes, ...plItems]; writeFileSync('output_pl.xml', builder.build(xmlTree), 'utf-8');
- 运行脚本直接生成两个文件
node split-xml.js
内容的提问来源于stack exchange,提问作者Radoslaw
相关产品推荐
相关产品推荐

