应使用xsl:for-each-group group-by="order_id"在CSV转XML第一轮即分组,配合xsl:output indent="yes"确保格式正确,避免group-adjacent和disable-output-escaping。

用 xsl:for-each-group 处理 CSV 中的层级关系
CSV 本身没有嵌套结构,但实际数据常隐含分组逻辑(比如同一 order_id 下多行 item)。直接用 xsl:for-each 会平铺所有行,无法生成 <order><items><item>...</item></items></order> 这类结构。必须先按关键字段分组。
常见错误是试图在 CSV 转 XML 后再用 XSLT 分组——此时已丢失原始行序和分组边界,容易错配。正确做法是在解析 CSV 的第一轮 XSLT 中就完成分组。
- 确保输入 CSV 已转为扁平 XML(如每行一个
<row>,每列一个子元素),且含可分组字段(如<order_id>1001</order_id>) - 使用
xsl:for-each-group group-by="order_id",而非group-adjacent——后者依赖物理相邻,而 CSV 行序不保证逻辑分组连续 - 分组内用
xsl:for-each select="current-group()"遍历该组所有行,再逐个映射为<item>
处理 CSV 头部缺失或动态列名时的 xsl:key 替代方案
标准 CSV 解析后若无固定列名(如首行非 header,或列顺序不固定),@column-name 类路径表达式会失效。此时不能硬写 row/col2,需用位置索引 + 映射表。
典型场景:同一 XSLT 要处理不同版本 CSV(v1 有 qty,v2 改为 quantity),又不想维护多套样式表。
- 预先用
xsl:key建立列名到索引的映射(如key('col-index', 'quantity')→3),但前提是 XML 化时保留了列定义 - 更稳妥的做法:在 CSV 转 XML 阶段就统一列名(例如用 Python 脚本预处理,把所有变体映射为
<quantity>),XSLT 只面向标准化 XML 工作 - 若必须纯 XSLT 处理,可用
xsl:variable name="headers" select="/csv/headers/column/text()"提取首行,再用index-of($headers, 'quantity')动态定位,但要求 XSLT 2.0+ 且首行明确
xsl:output method="xml" 必须设 indent="yes" 且禁用 disable-output-escaping
嵌套 XML 若格式混乱,后期解析易出错。但很多人忽略 XSLT 输出控制,导致生成的 XML 所有标签挤在一行,或属性值被双重转义(如 而非 <code><)。
尤其当 CSV 字段含 XML 特殊字符(<、&、")时,错误配置会让内容变成无效 XML。
-
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>是底线配置,indent="yes"让嵌套结构可读且利于调试 - 绝对避免
disable-output-escaping="yes",除非你 100% 确认字段内容是安全的 XML 片段——CSV 数据不可信,应让 XSLT 自动转义 - 如果字段含换行符(\n),XSLT 默认会保留,但某些 XML 解析器对文本节点中的 \n 敏感;可在模板中用
normalize-space()或replace(., '\n', ' ')(XSLT 2.0+)预处理
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" encoding="UTF-8"/>
<xsl:template match="/csv">
<orders>
<xsl:for-each-group select="row" group-by="order_id">
<order id="{current-grouping-key()}">
<items>
<xsl:for-each select="current-group()">
<item sku="{sku}" qty="{qty}"/>
</xsl:for-each>
</items>
</order>
</xsl:for-each-group>
</orders>
</xsl:template>
</xsl:stylesheet>
真正卡住人的往往不是语法,而是 CSV 到 XML 的中间表示是否保留了分组线索——如果解析时把所有行压成同级 <row> 却没带原始行号或上下文标记,后续 XSLT 再怎么写分组逻辑都可能漏项或错绑。









