如何在本地上下文中评估XSLT处理器传入的路径参数?
问题与解决方案
问题背景
源XML:
<A> <B>something</B> <B>something else</B> </A>
目标XML:
<C> <D>something</D> <D>something else</D> </C>
常规XSLT可实现上述转换,但使用Python的lxml处理器传入参数化路径时,参数始终在全局上下文(current()节点为根节点)中评估,无法得到预期结果。尝试过嵌套模板传参、字符串参数后评估等方法均无效,目前通过预处理XSL文件解决,希望找到纯XSLT+lxml的解决方案。
解决方案
方法一:使用XSLT 3.0的xsl:evaluate(灵活度高)
利用XSLT 3.0的xsl:evaluate特性,指定路径评估的上下文节点,避免参数在全局上下文执行。
XSLT样式表
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 可参数化的路径和目标元素名 --> <xsl:param name="source-path" select="'A/B'"/> <xsl:param name="root-target" select="'C'"/> <xsl:param name="item-target" select="'D'"/> <xsl:template match="/"> <!-- 生成根目标元素 --> <xsl:element name="{$root-target}"> <!-- 在当前上下文(根节点)评估传入的路径 --> <xsl:evaluate xpath="$source-path" context-item="."> <!-- 匹配路径选中的节点,生成目标子元素 --> <xsl:template match="*"> <xsl:element name="{$item-target}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> </xsl:evaluate> </xsl:element> </xsl:template> </xsl:stylesheet>
Python调用代码
from lxml import etree # 加载源XML source_xml = """<A> <B>something</B> <B>something else</B> </A>""" source_tree = etree.fromstring(source_xml) # 加载XSLT样式表 xslt_content = """<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:param name="source-path" select="'A/B'"/> <xsl:param name="root-target" select="'C'"/> <xsl:param name="item-target" select="'D'"/> <xsl:template match="/"> <xsl:element name="{$root-target}"> <xsl:evaluate xpath="$source-path" context-item="."> <xsl:template match="*"> <xsl:element name="{$item-target}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> </xsl:evaluate> </xsl:element> </xsl:template> </xsl:stylesheet>""" xslt_tree = etree.fromstring(xslt_content) # 创建转换器并传递参数 transformer = etree.XSLT(xslt_tree) result = transformer( source_tree, source_path="'A/B'", root_target="'C'", item_target="'D'" ) # 输出结果 print(str(result))
方法二:XSLT 1.0兼容方案(无版本依赖)
如果环境不支持XSLT 3.0,可通过传递节点名称参数,结合local-name()在当前上下文匹配节点。
XSLT样式表
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:param name="source-root-name" select="'A'"/> <xsl:param name="source-item-name" select="'B'"/> <xsl:param name="target-root-name" select="'C'"/> <xsl:param name="target-item-name" select="'D'"/> <xsl:template match="/"> <xsl:element name="{$target-root-name}"> <!-- 在当前上下文匹配指定名称的节点 --> <xsl:apply-templates select="*[local-name()=$source-root-name]/*[local-name()=$source-item-name]" /> </xsl:element> </xsl:template> <xsl:template match="*"> <xsl:element name="{$target-item-name}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> </xsl:stylesheet>
Python调用代码
from lxml import etree source_xml = """<A> <B>something</B> <B>something else</B> </A>""" source_tree = etree.fromstring(source_xml) xslt_content = """<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:param name="source-root-name" select="'A'"/> <xsl:param name="source-item-name" select="'B'"/> <xsl:param name="target-root-name" select="'C'"/> <xsl:param name="target-item-name" select="'D'"/> <xsl:template match="/"> <xsl:element name="{$target-root-name}"> <xsl:apply-templates select="*[local-name()=$source-root-name]/*[local-name()=$source-item-name]" /> </xsl:element> </xsl:template> <xsl:template match="*"> <xsl:element name="{$target-item-name}"> <xsl:value-of select="."/> </xsl:element> </xsl:template> </xsl:stylesheet>""" xslt_tree = etree.fromstring(xslt_content) transformer = etree.XSLT(xslt_tree) result = transformer( source_tree, source_root_name="'A'", source_item_name="'B'", target_root_name="'C'", target_item_name="'D'" ) print(str(result))
关键说明
- 避免直接将参数化路径写在
select属性中(如select="$source-path"),这种写法会让参数在全局上下文评估,而非当前模板的上下文,这是之前遇到问题的核心原因。 xsl:evaluate需要lxml版本支持XSLT 3.0,确保环境中的lxml版本足够新。- 第二种方法基于XSLT 1.0,兼容性更强,适合无法升级lxml的场景。
内容的提问来源于stack exchange,提问作者Stefanos Tsaousis
相关产品推荐
相关产品推荐

