如何用XSLT 1.0追踪XML继承树并生成有序Python类?
XSLT 1.0 处理XML Schema继承树生成Python类的解决方案
1. 递归模板追踪继承树
完全可以用递归<call-template name="supertype"/>实现继承树的追踪,核心逻辑是先处理父类,再处理子类,确保父类定义出现在子类之前。
实现思路
- 先定义一个
key快速通过类名查找对应的Schema类型节点:
<xsl:key name="type-by-name" match="xs:complexType" use="@name"/>
- 编写递归模板,先递归处理当前类的父类,再输出当前类的代码:
<xsl:template name="process-type"> <xsl:param name="type-name"/> <xsl:param name="processed-types"/> <!-- 记录已处理的类,用于去重 --> <xsl:variable name="current-type" select="key('type-by-name', $type-name)"/> <xsl:if test="$current-type"> <!-- 获取父类名称(适配extension/restriction两种继承方式) --> <xsl:variable name="supertype-name"> <xsl:choose> <xsl:when test="$current-type/xs:extension"> <xsl:value-of select="$current-type/xs:extension/@base"/> </xsl:when> <xsl:when test="$current-type/xs:restriction"> <xsl:value-of select="$current-type/xs:restriction/@base"/> </xsl:when> <xsl:otherwise/> </xsl:choose> </xsl:variable> <!-- 递归处理父类:如果父类存在且未处理,先调用模板处理父类 --> <xsl:if test="$supertype-name != '' and not(contains($processed-types, concat('|', $supertype-name, '|')))"> <xsl:call-template name="process-type"> <xsl:with-param name="type-name" select="$supertype-name"/> <xsl:with-param name="processed-types" select="$processed-types"/> </xsl:call-template> </xsl:if> <!-- 输出当前类的Python代码(仅当未处理过时) --> <xsl:if test="not(contains($processed-types, concat('|', $type-name, '|')))"> <xsl:text>class </xsl:text> <xsl:value-of select="$type-name"/> <xsl:if test="$supertype-name != ''"> <xsl:text>(</xsl:text> <xsl:value-of select="$supertype-name"/> <xsl:text>):</xsl:text> </xsl:if> <xsl:text> # 生成类属性/方法的逻辑 </xsl:text> <!-- 更新已处理类的标记 --> <xsl:variable name="new-processed" select="concat($processed-types, '|', $type-name, '|')"/> <!-- 后续模板调用时传递new-processed作为更新后的状态 --> </xsl:if> </xsl:if> </xsl:template>
这个递归会从目标子类向上遍历到顶层父类,再从顶层开始依次输出,保证父类定义先于子类。
2. 检查类是否已生成(避免重复输出)
XSLT 1.0没有可变变量,需通过状态传递或节点集实现已处理类的追踪:
方法1:字符串标记传递
初始化一个带分隔符的字符串(比如用|包裹类名),在递归调用中传递更新后的字符串:
<!-- 初始状态:空标记,用|作为首尾分隔符避免匹配子串 --> <xsl:variable name="initial-processed" select="'|'"/>
调用模板时传入该变量,每次处理完类后拼接新的类名到标记中,用contains($processed-types, concat('|', $type-name, '|'))判断是否已处理。
方法2:节点集记录(更可靠,需处理器支持EXSLT)
如果你的XSLT处理器支持EXSLT的exsl:node-set,可以维护一个节点列表记录已处理类:
<!-- 初始化空节点集 --> <xsl:variable name="processed-nodes"> <processed-types/> </xsl:variable> <!-- 标记类为已处理的辅助模板 --> <xsl:template name="mark-processed"> <xsl:param name="type-name"/> <xsl:variable name="current-set" select="exsl:node-set($processed-nodes)"/> <xsl:copy-of select="$current-set"/> <type><xsl:value-of select="$type-name"/></type> </xsl:template>
检查时判断exsl:node-set($processed-nodes)/type[text()=$type-name]是否存在,存在则跳过输出。
注意事项
- 加入循环检测:如果Schema中存在循环继承(虽然XML Schema规范不允许,但需防异常),可在递归中加入路径追踪,避免无限递归。
- 遍历所有类:最终调用时,遍历所有
xs:complexType[@name]节点,逐个调用process-type模板,确保所有类都被处理。
内容的提问来源于stack exchange,提问作者FlyingTurbine
相关产品推荐
相关产品推荐

