如何用XSLT将两个逗号分隔标签拆分为父子标签列表?
解决逗号分隔字符串拆分为平级标签列表的问题
我懂你现在的困扰——你想把<UserID>162,163</UserID>和<UserName>Stacy,Stephen</UserName>这两个逗号分隔的字符串,转换成一组平级的<User>标签,每个<User>里对应包含<ID>和<Name>,但当前的XSLT模板生成了嵌套的<User>结构,而不是你想要的列表形式对吧?
问题根源分析
你当前的模板把<User>标签放在了递归调用的内部,每次递归生成新的<User>时都会嵌套到上一个<User>里面,自然就出现了嵌套结构。正确的思路应该是先把两个字符串分别拆成对应位置的序列/节点集,再逐个匹配生成平级的
下面分两种XSLT版本给出解决方案:
方案1:XSLT 1.0 兼容版本(适合旧版处理器)
XSLT 1.0没有内置的字符串拆分函数,我们需要用递归模板先把两个字符串拆成节点集,再通过位置匹配来生成对应
第一步:通用字符串拆分模板
这个模板会把输入的逗号分隔字符串转换成<item>节点的集合:
<xsl:template name="tokenize-string"> <xsl:param name="text" select="''"/> <xsl:param name="separator" select="','"/> <xsl:if test="$text != ''"> <!-- 提取当前分隔符前的内容,去除前后空格 --> <item> <xsl:value-of select="normalize-space(substring-before(concat($text, $separator), $separator))"/> </item> <!-- 递归处理剩余的字符串 --> <xsl:call-template name="tokenize-string"> <xsl:with-param name="text" select="substring-after($text, $separator)"/> <xsl:with-param name="separator" select="$separator"/> </xsl:call-template> </xsl:if> </xsl:template>
第二步:主模板生成列表
调用上面的模板拆分ID和Name,再遍历匹配位置生成平级
<xsl:template match="/"> <Users> <!-- 拆分UserID为节点集 --> <xsl:variable name="id-nodes"> <xsl:call-template name="tokenize-string"> <xsl:with-param name="text" select="UserID"/> </xsl:call-template> </xsl:variable> <!-- 拆分UserName为节点集 --> <xsl:variable name="name-nodes"> <xsl:call-template name="tokenize-string"> <xsl:with-param name="text" select="UserName"/> </xsl:call-template> </xsl:variable> <!-- 遍历ID节点,按位置匹配对应的Name节点 --> <xsl:for-each select="$id-nodes/item"> <xsl:variable name="current-pos" select="position()"/> <User> <ID><xsl:value-of select="."/></ID> <Name><xsl:value-of select="$name-nodes/item[$current-pos]"/></Name> </User> </xsl:for-each> </Users> </xsl:template>
方案2:XSLT 2.0+ 简化版本(适合新版处理器)
XSLT 2.0及以上版本内置了tokenize()函数,可以直接把字符串拆成序列,代码会简洁很多:
<xsl:template match="/"> <Users> <!-- 拆分ID和Name为字符串序列,自动处理逗号前后的空格 --> <xsl:variable name="id-sequence" select="tokenize(normalize-space(UserID), '\s*,\s*')"/> <xsl:variable name="name-sequence" select="tokenize(normalize-space(UserName), '\s*,\s*')"/> <!-- 遍历序列位置,生成对应<User> --> <xsl:for-each select="1 to count($id-sequence)"> <xsl:variable name="current-pos" select="."/> <User> <ID><xsl:value-of select="$id-sequence[$current-pos]"/></ID> <Name><xsl:value-of select="$name-sequence[$current-pos]"/></Name> </User> </xsl:for-each> </Users> </xsl:template>
效果验证
对于输入:
<Root> <UserID>162,163</UserID> <UserName>Stacy,Stephen</UserName> </Root>
两个方案都会输出你期望的结果:
<Users> <User> <ID>162</ID> <Name>Stacy</Name> </User> <User> <ID>163</ID> <Name>Stephen</Name> </User> </Users>
内容的提问来源于stack exchange,提问作者Agnus
相关产品推荐
相关产品推荐

