如何使用XSL生成类CSV格式?附相关XSL代码片段
嘿,我来帮你搞定用XSL生成标准CSV格式的事儿!看你贴的代码片段,应该是在处理CD目录数据,但目前的逻辑有点小问题——比如重复判断最后一个cd元素,而且没处理字段分隔、换行,更重要的是没考虑CSV里的特殊字符(这可是踩坑重灾区)。
先给你一个完整的、健壮的实现,然后拆解关键部分:
完整XSL代码示例
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 匹配根目录节点,开始生成CSV --> <xsl:template match="/catalog"> <!-- 先输出CSV表头,和你的CD字段对应,可按需修改 --> <xsl:text>Artist,Title,Year,Price
</xsl:text> <!-- 遍历每一张CD --> <xsl:for-each select="cd"> <!-- 依次输出每个字段,用逗号分隔 --> <xsl:apply-templates select="artist"/> <xsl:text>,</xsl:text> <xsl:apply-templates select="title"/> <xsl:text>,</xsl:text> <xsl:apply-templates select="year"/> <xsl:text>,</xsl:text> <xsl:apply-templates select="price"/> <!-- 除了最后一行,每行末尾加换行符 --> <xsl:if test="position() != last()"> <xsl:text>
</xsl:text> </xsl:if> </xsl:for-each> </xsl:template> <!-- 核心:处理CSV特殊字符,避免解析错误 --> <xsl:template match="text()"> <xsl:choose> <!-- 如果字段包含逗号或双引号,用双引号包裹,内部双引号转成两个(CSV标准要求) --> <xsl:when test="contains(., ',') or contains(., '"')"> <xsl:text>"</xsl:text> <xsl:value-of select="translate(., '"', '""')"/> <xsl:text>"</xsl:text> </xsl:when> <!-- 普通内容直接输出 --> <xsl:otherwise> <xsl:value-of select="."/> </xsl:otherwise> </xsl:choose> </xsl:template> </xsl:stylesheet>
关键细节说明
表头与换行:
先定义好CSV的列名,和你的XML字段一一对应。用
表示Windows风格的换行(如果是Unix/Linux环境,只用
就行),确保生成的CSV能被表格软件正确识别。字段分隔与行处理:
每个cd元素对应CSV的一行,字段之间用逗号分隔。最后一行不输出多余的换行,避免生成空行。特殊字符处理:
这是最容易忽略的点!如果你的CD字段里有逗号(比如艺术家名字是"Doe, John")或者双引号,直接输出会导致CSV解析错乱。上面的代码会自动把这类字段用双引号包裹,并且把字段内部的双引号转成两个(比如"He said ""Hello"""),完全符合CSV标准。
如果你需要动态适配字段
如果你的XML中cd的子元素不是固定的(比如可能新增字段),可以改成动态遍历的方式,不用硬编码字段名:
<!-- 动态生成表头 --> <xsl:for-each select="cd[1]/*"> <xsl:value-of select="name()"/> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> <xsl:text>
</xsl:text> <!-- 动态遍历每个CD的子元素 --> <xsl:for-each select="cd"> <xsl:for-each select="*"> <xsl:apply-templates/> <xsl:if test="position() != last()"> <xsl:text>,</xsl:text> </xsl:if> </xsl:for-each> <xsl:if test="position() != last()"> <xsl:text>
</xsl:text> </xsl:if> </xsl:for-each>
你的原有代码问题分析
你原来的代码里,重复判断了position() = last(),而且直接取./child::*会把所有子元素的内容连在一起,没有分隔,这样生成的内容根本不是标准的CSV格式。另外缺少特殊字符处理,很容易出问题。
内容的提问来源于stack exchange,提问作者Hat hout
相关产品推荐
相关产品推荐

