基于Saxon 11HE的多扁平XML转树形XML的XSLT实现问题
基于Saxon 11HE的扁平XML转树形结构优化方案
针对你提到的三个核心问题,结合Saxon 11HE对XSLT 3.0的支持,以下是高效且简洁的解决方案:
核心优化思路
先通过索引预构建减少重复查询,再通过变量与key机制切换上下文,最后嵌入排序逻辑完成层级转换。
预定义索引(解决重复查询问题)
在XSLT顶层定义两个索引,利用Saxon的预构建索引特性实现O(1)级查询效率:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <!-- 按OBJECTID索引1.xml中的地址对象 --> <xsl:key name="object-by-id" match="OBJECT" use="@OBJECTID"/> <!-- 按PARENTOBJID索引2.xml中的层级关联 --> <xsl:key name="child-items" match="ITEM" use="@PARENTOBJID"/> <!-- 预加载两个输入文档到变量,避免重复调用document() --> <xsl:variable name="address-data" select="document('1.xml')"/> <xsl:variable name="hierarchy-data" select="document('2.xml')"/>
问题1:在xsl:for-each中切换回1.xml的上下文
有两种直接可行的方式:
- 通过key指定上下文:不需要切换整个for-each的上下文,直接在查询时指定1.xml作为key的作用域:
<!-- 示例:在遍历2.xml的ITEM时,获取对应1.xml的OBJECT节点 --> <xsl:variable name="current-object" select="key('object-by-id', @OBJECTID, $address-data)"/> - 直接遍历变量中的1.xml节点:如果需要在for-each中直接操作1.xml的节点,将select指向预加载的变量即可:
<xsl:for-each select="$address-data//OBJECT[@LEVEL='REGION']"> <!-- 此处上下文即为1.xml中的OBJECT节点 --> </xsl:for-each>
问题2:高效匹配对象属性,避免重复查询
核心就是利用前面定义的两个索引:
- 查询某个OBJECTID对应的地址对象:
key('object-by-id', $target-id, $address-data) - 查询某个父节点下的所有子关联:
key('child-items', $parent-id, $hierarchy-data)
Saxon会在文档加载时一次性构建索引,后续查询无需重复遍历整个文档,大数据量场景下性能提升明显。
问题3:按OBJECT的@NAME属性排序
在遍历节点的<xsl:for-each>中嵌入<xsl:sort>元素,直接关联对应OBJECT的NAME属性即可。如果是中文排序,可添加lang="zh"确保排序规则符合语言习惯:
<xsl:for-each select="key('child-items', $parent-id, $hierarchy-data)"> <xsl:variable name="child-object" select="key('object-by-id', @OBJECTID, $address-data)"/> <!-- 按NAME排序,支持多语言规则 --> <xsl:sort select="$child-object/@NAME" lang="zh"/> <!-- 生成对应层级的元素 --> <xsl:element name="{translate($child-object/@LEVEL, ' ', '_')}"> <xsl:copy-of select="$child-object/@*"/> <!-- 递归处理子节点 --> <xsl:call-template name="process-children"> <xsl:with-param name="parent-id" select="@OBJECTID"/> </xsl:call-template> </xsl:element> </xsl:for-each>
完整递归转换模板
<xsl:template match="/"> <ROOT> <!-- 处理顶级节点(PARENTOBJID为空的层级) --> <xsl:for-each select="$hierarchy-data//ITEM[not(@PARENTOBJID) or @PARENTOBJID='']"> <xsl:variable name="top-object" select="key('object-by-id', @OBJECTID, $address-data)"/> <xsl:element name="{translate($top-object/@LEVEL, ' ', '_')}"> <xsl:copy-of select="$top-object/@*"/> <xsl:call-template name="process-children"> <xsl:with-param name="parent-id" select="@OBJECTID"/> </xsl:call-template> </xsl:element> </xsl:for-each> </ROOT> </xsl:template> <xsl:template name="process-children"> <xsl:param name="parent-id"/> <!-- 遍历当前父节点的所有子关联并排序 --> <xsl:for-each select="key('child-items', $parent-id, $hierarchy-data)"> <xsl:variable name="child-object" select="key('object-by-id', @OBJECTID, $address-data)"/> <xsl:sort select="$child-object/@NAME"/> <xsl:element name="{translate($child-object/@LEVEL, ' ', '_')}"> <xsl:copy-of select="$child-object/@*"/> <!-- 递归处理下一级子节点 --> <xsl:call-template name="process-children"> <xsl:with-param name="parent-id" select="@OBJECTID"/> </xsl:call-template> </xsl:element> </xsl:for-each> </xsl:template> </xsl:stylesheet>
内容的提问来源于stack exchange,提问作者Sergey Zakharov
相关产品推荐
相关产品推荐

