SAXON-HE 10.6下用XSLT将同名元素追加序号转为属性的方法
问题描述
- 待转换的源XML文件内容:
<?xml version="1.0" encoding="UTF-8"?> <toplevel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <address> <line>address line a</line> <line>address line b</line> <line>address line c</line> <line>address line d</line> </address> </toplevel>
- 源XML结构遵循的XSD Schema定义:
<?xml version="1.0" encoding="UTF-8"?> <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"> <xs:element name="toplevel"> <xs:complexType> <xs:sequence> <xs:element ref="address"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="address"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="4" ref="line"/> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="line" type="xs:string"/> </xs:schema>
- 目标转换结果格式(使用SAXON-HE 10.6执行XSLT转换实现):
<?xml version="1.0" encoding="UTF-8"?> <toplevel xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <address line1="address line a" line2="address line b" line3="address line c" line4="address line d"/> </toplevel>
- 现有XSLT存在的问题:仅当源XML中line元素自带编号属性时可正常运行,实际源数据的line元素无内置编号;尝试添加
xsl:for-each逻辑、定义变量均无法实现为生成的line属性追加连续序号的效果。已知line元素最大数量固定为4,支持硬编码实现,但不清楚对应语法,需要可在SAXON-HE 10.6中直接运行的可行方案。 - 现有存在问题的XSLT代码:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0"> <xsl:output method="xml" indent="yes" /> <xsl:strip-space elements="*"/> <xsl:template match="/"> <xsl:text>
</xsl:text> <xsl:apply-templates mode="copy" select="."/> </xsl:template> <xsl:template match="* | text() | @*" mode="copy"> <xsl:copy> <xsl:apply-templates mode="copy" select="@*"/> <xsl:apply-templates mode="copy"/> </xsl:copy> </xsl:template> <xsl:template match="*"/> <xsl:template match="toplevel" mode="copy"> <xsl:copy> <xsl:apply-templates mode="copy" select="address"/> </xsl:copy> </xsl:template> <xsl:template match="address" mode="copy"> <xsl:copy> <xsl:apply-templates mode="convert-to-attr" select="*"/> </xsl:copy> </xsl:template> <xsl:template match="*" mode="convert-to-attr"> <xsl:attribute name="{name()}" select="text()"/> </xsl:template> </xsl:stylesheet>
实现方案
问题核心是属性生成逻辑直接使用了line元素本身的名称,没有追加遍历序号。XSLT内置的position()函数可以直接获取当前处理节点在选中节点集合中的顺序位置,不需要额外定义变量,也不需要复杂循环,修改convert-to-attr模式的模板即可实现需求,完全兼容SAXON-HE 10.6的XSLT 2.0支持规范。
修正后的完整XSLT代码如下:
<?xml version="1.0" encoding="UTF-8"?> <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0"> <xsl:output method="xml" indent="yes" /> <xsl:strip-space elements="*"/> <xsl:template match="/"> <xsl:text>
</xsl:text> <xsl:apply-templates mode="copy" select="."/> </xsl:template> <xsl:template match="* | text() | @*" mode="copy"> <xsl:copy> <xsl:apply-templates mode="copy" select="@*"/> <xsl:apply-templates mode="copy"/> </xsl:copy> </xsl:template> <xsl:template match="*"/> <xsl:template match="toplevel" mode="copy"> <xsl:copy> <xsl:apply-templates mode="copy" select="address"/> </xsl:copy> </xsl:template> <xsl:template match="address" mode="copy"> <xsl:copy> <xsl:apply-templates mode="convert-to-attr" select="line"/> </xsl:copy> </xsl:template> <!-- 核心修改:用position()获取序号拼接属性名 --> <xsl:template match="line" mode="convert-to-attr"> <xsl:attribute name="line{position()}" select="."/> </xsl:template> </xsl:stylesheet>
硬编码实现版本(可选)
如果需要严格固定生成4个line属性,哪怕源文件中line数量不足4也生成对应空值属性,可以直接硬编码选取对应位置的line节点,替换上述代码中address匹配的模板即可:
<xsl:template match="address" mode="copy"> <xsl:copy> <xsl:attribute name="line1" select="line[1]"/> <xsl:attribute name="line2" select="line[2]"/> <xsl:attribute name="line3" select="line[3]"/> <xsl:attribute name="line4" select="line[4]"/> </xsl:copy> </xsl:template>
内容的提问来源于stack exchange,提问作者blm99
相关产品推荐
相关产品推荐

