XSLT 2.0如何高效将指定高Unicode字符转换为处理指令
高Unicode文本转处理指令优化方案
问题梳理
- 需求:对同时包含7位ASCII、高Unicode字符的文本节点做处理,将指定高Unicode字符转换为对应处理指令,转换规则示例:
‑ -> <processing-instruction name="xpp">character output="hyphen"</processing-instruction>  -> <processing-instruction name="xpp">character output="page"</processing-instruction>
- 原有方案问题:
- 用
xsl:tokenize逐次按单个字符拆分,第一次拆分后变量就会包含普通文本+处理指令的混合内容,后续分词调用会直接报错 - 每新增一个待转换字符就要新增一层中间变量、重复调用分词模板,代码冗余度极高,扩展性差
- 处理指令通过转义尖括号拼接字符串生成,不符合XSLT原生节点生成规范,容易出现转义错误
- 未覆盖
x00-x1F、0xFF及以上范围非配置字符的自动标记需求
- 用
原有冗余实现代码如下:
<xsl:template match="text()"> <xsl:variable name="SR1"> <xsl:value-of select="fn:replace(., '%', '\\%')"/> </xsl:variable> <!-- 非断字连字符 --> <xsl:variable name="SR2"> <xsl:call-template name="tokenize"> <xsl:with-param name="string" select="$SR1"/> <xsl:with-param name="delimiter">‑</xsl:with-param> <xsl:with-param name="PI"><xsl:text><?xpp character symbol="bxhyphen" hex="x2011" data="E28091"?></xsl:text></xsl:with-param> </xsl:call-template> </xsl:variable> <!-- 页码引用 --> <xsl:variable name="SR3"> <xsl:call-template name="tokenize"> <xsl:with-param name="string" ><xsl:copy-of select="$SR2"/></xsl:with-param> <xsl:with-param name="delimiter"></xsl:with-param> <xsl:with-param name="PI"><xsl:text><?xpp character symbol="pgref" hex="xF0B7" data="EF82B7"?></xsl:text> </xsl:with-param> </xsl:call-template> </xsl:variable> <!-- 实心圆点 --> <xsl:variable name="SR4"> <xsl:call-template name="tokenize"> <xsl:with-param name="string" ><xsl:copy-of select="$SR3"/></xsl:with-param> <xsl:with-param name="delimiter">●</xsl:with-param> <xsl:with-param name="PI"><xsl:text><?xpp character symbol="bub" hex="x25CF" data="E2978F"?></xsl:text> </xsl:with-param> </xsl:call-template> </xsl:variable> <xsl:copy-of select="$SR4"/> </xsl:template>
优化实现
核心逻辑采用逐字符递归遍历,配合全局可配置映射表,单次遍历即可完成%转义、指定字符转PI、未映射特殊字符标记全流程,无需多层中间变量,直接通过XSLT原生语法生成PI节点。
1. 定义全局可配置字符映射表
所有待转换字符的映射规则统一配置,后续增删改规则无需调整处理逻辑:
<!-- 特殊字符映射配置:按需增删entry节点即可 --> <xsl:variable name="charMap"> <entry hex="2011" symbol="bxhyphen" data="E28091"/> <entry hex="F0B7" symbol="pgref" data="EF82B7"/> <entry hex="25CF" symbol="bub" data="E2978F"/> </xsl:variable>
2. 核心递归处理模板
<xsl:template name="processText"> <xsl:param name="input"/> <xsl:param name="pos" select="1"/> <!-- 递归终止:所有字符处理完成 --> <xsl:if test="$pos <= string-length($input)"> <xsl:variable name="curChar" select="substring($input, $pos, 1)"/> <xsl:variable name="curCode" select="string-to-codepoints($curChar)[1]"/> <xsl:choose> <!-- 第一步:转义%特殊字符 --> <xsl:when test="$curChar = '%'"> <xsl:text>\%</xsl:text> </xsl:when> <!-- 第二步:匹配已配置特殊字符,原生生成PI节点 --> <xsl:when test="$charMap/entry[@hex = upper-case(format-number($curCode, 'X'))]"> <xsl:variable name="matchEntry" select="$charMap/entry[@hex = upper-case(format-number($curCode, 'X'))][1]"/> <xsl:processing-instruction name="xpp"> <xsl:text>character symbol="</xsl:text> <xsl:value-of select="$matchEntry/@symbol"/> <xsl:text>" hex="x</xsl:text> <xsl:value-of select="$matchEntry/@hex"/> <xsl:text>" data="</xsl:text> <xsl:value-of select="$matchEntry/@data"/> <xsl:text>"</xsl:text> </xsl:processing-instruction> </xsl:when> <!-- 第三步:标记未配置的特殊字符:x00-x1F、0xFF及以上范围 --> <xsl:when test="($curCode >= 0 and $curCode <= 31) or $curCode >= 255"> <xsl:processing-instruction name="xpp"> <xsl:text>unmapped-char hex="x</xsl:text> <xsl:value-of select="upper-case(format-number($curCode, 'X'))"/> <xsl:text>"</xsl:text> </xsl:processing-instruction> </xsl:when> <!-- 普通ASCII字符直接输出 --> <xsl:otherwise> <xsl:value-of select="$curChar"/> </xsl:otherwise> </xsl:choose> <!-- 递归处理下一个字符 --> <xsl:call-template name="processText"> <xsl:with-param name="input" select="$input"/> <xsl:with-param name="pos" select="$pos + 1"/> </xsl:call-template> </xsl:if> </xsl:template>
3. 替换原文本节点匹配模板
原有多层嵌套逻辑可直接简化为单模板调用,原有自定义tokenize模板可完全废弃:
<xsl:template match="text()"> <xsl:call-template name="processText"> <xsl:with-param name="input" select="."/> </xsl:call-template> </xsl:template>
方案优势
- 扩展性强:新增转换规则仅需在
charMap中添加对应条目,无需修改核心处理逻辑 - 性能更优:单次遍历完成所有处理,无多次字符串拆分、中间变量拷贝开销,从根源避免混合内容导致的分词报错
- 规范性好:通过
xsl:processing-instruction原生生成PI节点,无需手动转义尖括号拼接字符串,杜绝转义错误 - 覆盖全需求:自动实现
%转义、指定字符转换、未映射特殊字符标记功能,无需额外开发
内容的提问来源于stack exchange,提问作者markh
相关产品推荐
相关产品推荐

