使用XSLT contains函数引用变量时的节点过滤问题求助
问题描述
我正在学习XML/XSLT,若有错误之处敬请谅解。我尝试基于文件内的类型主列表过滤XML节点,将类型列表存入变量后,在contains函数中引用该变量时过滤结果不符合预期。
预期移除Alex和Jenny,但仅移除了Jenny,Alex的type为3被误判存在于列表中(实际是列表中的1603的字符3),推测contains是匹配单个字符而非完整类型,请问如何实现匹配完整类型?
XML文件
<?xml version="1.0" encoding="UTF-8"?> <users> <types>1600,1601,1602,1603</types> <user><name>Joe</name><type>1600</type></user> <user><name>Susan</name><type>1601</type></user> <user><name>Alex</name><type>3</type></user> <user><name>Jenny</name><type>4</type></user> </users>
现有XSLT代码
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <users> <!-- store type list in variable --> <xsl:variable name="var_typelist" select="users/types"/> <!-- cycle through users --> <xsl:for-each select="users/user"> <!-- store user data in a variable --> <xsl:variable name="var_user" select="*"></xsl:variable> <!-- check if user type matches one in the list --> <xsl:choose> <xsl:when test="contains($var_typelist,type)"> <!-- rebuild file with user details --> <users> <xsl:copy-of select="$var_user"/> </users> </xsl:when> <xsl:otherwise> </xsl:otherwise> </xsl:choose> </xsl:for-each> </users> </xsl:template> </xsl:stylesheet>
解决方案
方法1:使用tokenize拆分类型列表(XSLT2.0+推荐)
contains是子字符串匹配,所以会把"3"误判为"1603"的一部分。XSLT2.0支持tokenize函数,可将逗号分隔的字符串拆分为字符串序列,直接判断用户类型是否属于该序列即可。
修改后的代码:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <users> <!-- 将类型列表拆分为字符串序列 --> <xsl:variable name="valid_types" select="tokenize(users/types, ',')"/> <!-- 直接筛选类型匹配的用户 --> <xsl:for-each select="users/user[type = $valid_types]"> <user> <xsl:copy-of select="*"/> </user> </xsl:for-each> </users> </xsl:template> </xsl:stylesheet>
tokenize(users/types, ',')会把1600,1601,1602,1603转换为序列('1600','1601','1602','1603')- 通过
user[type = $valid_types]直接过滤出符合条件的用户,代码更简洁高效。
方法2:添加分隔符避免子字符串匹配
如果无法使用tokenize,可以给类型列表和用户类型前后都加上逗号,确保匹配的是完整的类型值。
修改后的判断逻辑及完整代码:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:template match="/"> <users> <xsl:variable name="var_typelist" select="users/types"/> <!-- 给类型列表前后添加逗号,构建完整匹配的字符串 --> <xsl:variable name="full_typelist" select="concat(',', $var_typelist, ',')"/> <xsl:for-each select="users/user"> <xsl:choose> <!-- 判断带逗号包裹的用户类型是否存在于处理后的类型列表中 --> <xsl:when test="contains($full_typelist, concat(',', type, ','))"> <user> <xsl:copy-of select="*"/> </user> </xsl:when> </xsl:choose> </xsl:for-each> </users> </xsl:template> </xsl:stylesheet>
- 处理后的类型列表为
,1600,1601,1602,1603,,用户类型"3"会变成,3,,此时contains只会匹配完整的,3,,而该值不在处理后的列表中,Alex会被正确过滤。
内容的提问来源于stack exchange,提问作者AJW
相关产品推荐
相关产品推荐

