如何在XSLT工作流中注入XML预处理过滤步骤并复用原有模板
问题
现有一套XSLT与XML处理工作流,从根匹配节点开始触发后续处理。需要在文档处理前注入预处理步骤,在子节点层级过滤数据后再执行原有流程。
当前流程
输入A(xmlA)-> 模板(xslt1)= 输出
预处理需求
输入A(xmlA)-> 过滤无用数据(xmlNEW)-> 模板(xslt1)= 不含过滤数据的输出
由于现有模板数量过多,在后续流程中过滤会导致模板失效,因此必须在处理前完成过滤。已单独实现过滤功能,但尝试用变量和参数调用模板整合时无法正常工作。
示例XML
<?xml version="1.0" encoding="UTF-16"?> <MultiDocumentLevel> <DocumentLevel> <OrderLevel> <AllowedFormat> <HeaderLevel> <HeaderSort>12345</HeaderSort> <HeaderCode>12345</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>9999</HeaderSort> <HeaderCode>9999-1</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>0000</HeaderSort> <HeaderCode>0000</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>8989</HeaderSort> <HeaderCode>8989</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>7777</HeaderSort> <HeaderCode>123</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>123</HeaderSort> <HeaderCode>14</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>1</HeaderSort> <HeaderCode>1</HeaderCode> </HeaderLevel> </AllowedFormat> </OrderLevel> </DocumentLevel> </MultiDocumentLevel>
主模板(xslt1)
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="html"/> <xsl:template match="/"> <xsl:apply-templates select="MultiDocumentLevel"/> </xsl:template> <!-- ****************************************************************************************************************** --> <!-- Name: MultiDocumentLevel --> <!-- Xsl's Main Template - Start of Builder --> <!-- ****************************************************************************************************************** --> <xsl:template match="MultiDocumentLevel"> <xsl:apply-templates select="DocumentLevel/OrderLevel/AllowedFormat/HeaderLevel"/> </xsl:template> <!-- ****************************************************************************************************************** --> <!-- Name: HeaderLevel --> <!-- Xsl's HeaderLevel Template - Process main data --> <!-- ****************************************************************************************************************** --> <xsl:template match="HeaderLevel"> <!-- Process all nodes under HeaderLevel--> <xsl:value-of select="concat(position(),' - ',HeaderCode,' - ',.)"/><br/> </xsl:template> </xsl:stylesheet>
过滤模板(单独实现)
<?xml version="1.0"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt"> <xsl:output method="xml" indent="yes"/> <xsl:variable name="allowedheaders"> <headers status="AllowedToPrint"> <HeaderSort printHeader="12345" name="Allowed1"/> <HeaderSort printHeader="123" name="Allowed2"/> <HeaderSort printHeader="0000" name="Allowed3"/> </headers> </xsl:variable> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()" /> </xsl:copy> </xsl:template> <xsl:template match="/"> <xsl:apply-templates select="@*|node()" /> </xsl:template> <xsl:template match="HeaderLevel"> <!-- filter the data at headerlevel based on lookup in allowedheaders variable --> <xsl:if test="HeaderSort=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader or HeaderCode=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:if> </xsl:template> </xsl:stylesheet>
期望过滤后的XML(供主模板处理)
<MultiDocumentLevel> <DocumentLevel> <OrderLevel> <AllowedFormat> <HeaderLevel> <HeaderSort>12345</HeaderSort> <HeaderCode>12345</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>0000</HeaderSort> <HeaderCode>0000</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>7777</HeaderSort> <HeaderCode>123</HeaderCode> </HeaderLevel> <HeaderLevel> <HeaderSort>123</HeaderSort> <HeaderCode>14</HeaderCode> </HeaderLevel> </AllowedFormat> </OrderLevel> </DocumentLevel> </MultiDocumentLevel>
解决方案
基于XSLT 1.0特性,提供两种无需修改原有主模板核心逻辑的整合方案:
方法1:导入主模板,修改根模板注入过滤逻辑
创建新的整合XSLT,先执行过滤生成节点集,再将其传递给原有主模板处理,无需修改xslt1文件:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="html"/> <!-- 导入原有主模板 --> <xsl:import href="xslt1.xsl"/> <!-- 过滤规则变量 --> <xsl:variable name="allowedheaders"> <headers status="AllowedToPrint"> <HeaderSort printHeader="12345" name="Allowed1"/> <HeaderSort printHeader="123" name="Allowed2"/> <HeaderSort printHeader="0000" name="Allowed3"/> </headers> </xsl:variable> <!-- 覆盖根模板,先过滤再调用原有逻辑 --> <xsl:template match="/"> <!-- 第一步:生成过滤后的XML节点集 --> <xsl:variable name="filteredXml"> <xsl:apply-templates select="@*|node()" mode="filter"/> </xsl:variable> <!-- 第二步:将过滤后的节点传递给原有主模板处理 --> <xsl:apply-templates select="msxsl:node-set($filteredXml)/MultiDocumentLevel"/> </xsl:template> <!-- 过滤模式:复制所有节点,仅处理HeaderLevel --> <xsl:template match="@*|node()" mode="filter"> <xsl:copy> <xsl:apply-templates select="@*|node()" mode="filter"/> </xsl:copy> </xsl:template> <!-- 过滤模式下的HeaderLevel:仅保留符合条件的节点 --> <xsl:template match="HeaderLevel" mode="filter"> <xsl:if test="HeaderSort=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader or HeaderCode=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader"> <xsl:copy> <xsl:apply-templates select="@*|node()" mode="filter"/> </xsl:copy> </xsl:if> </xsl:template> </xsl:stylesheet>
方法2:在主模板中封装过滤命名模板
如果无法导入外部模板,可直接修改主模板的根节点,添加过滤逻辑:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:fo="http://www.w3.org/1999/XSL/Format"> <xsl:output method="html"/> <!-- 添加过滤变量和命名模板 --> <xsl:variable name="allowedheaders"> <headers status="AllowedToPrint"> <HeaderSort printHeader="12345" name="Allowed1"/> <HeaderSort printHeader="123" name="Allowed2"/> <HeaderSort printHeader="0000" name="Allowed3"/> </headers> </xsl:variable> <!-- 过滤用命名模板 --> <xsl:template name="filterXml"> <xsl:param name="inputNode"/> <xsl:apply-templates select="$inputNode" mode="filter"/> </xsl:template> <xsl:template match="@*|node()" mode="filter"> <xsl:copy> <xsl:apply-templates select="@*|node()" mode="filter"/> </xsl:copy> </xsl:template> <xsl:template match="HeaderLevel" mode="filter"> <xsl:if test="HeaderSort=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader or HeaderCode=msxsl:node-set($allowedheaders)/headers[@status='AllowedToPrint']/HeaderSort/@printHeader"> <xsl:copy> <xsl:apply-templates select="@*|node()" mode="filter"/> </xsl:copy> </xsl:if> </xsl:template> <!-- 修改根模板,先过滤再执行原有逻辑 --> <xsl:template match="/"> <xsl:variable name="filteredXml"> <xsl:call-template name="filterXml"> <xsl:with-param name="inputNode" select="."/> </xsl:call-template> </xsl:variable> <xsl:apply-templates select="msxsl:node-set($filteredXml)/MultiDocumentLevel"/> </xsl:template> <!-- 原有主模板代码保留 --> <xsl:template match="MultiDocumentLevel"> <xsl:apply-templates select="DocumentLevel/OrderLevel/AllowedFormat/HeaderLevel"/> </xsl:template> <xsl:template match="HeaderLevel"> <xsl:value-of select="concat(position(),' - ',HeaderCode,' - ',.)"/><br/> </xsl:template> </xsl:stylesheet>
关键说明
- 利用mode属性区分过滤逻辑与原有业务逻辑,避免模板匹配冲突
- XSLT1.0中需通过
msxsl:node-set()将结果树片段转换为可查询的节点集(不同处理器可能对应不同函数,如Saxon的exsl:node-set()) - 两种方案均无需修改原有主模板的业务代码,仅在入口处注入过滤步骤
内容的提问来源于stack exchange,提问作者MergatroidSA
相关产品推荐
相关产品推荐

