XSLT构建无重复元素的XSD层级视图问题求助
解决XSD查看器中子元素重复显示的XSLT方案
针对你开发XSD查看器时遇到的同一父路径下xs:element因choice/sequence嵌套重复显示的问题,这里提供两种直接可行的XSLT 3.0解决思路:
方法一:使用xsl:for-each-group分组去重
核心逻辑:针对当前父元素,递归收集其所有嵌套在sequence/choice等容器中的子元素,按元素名称分组后仅处理每组第一个实例。
示例代码片段:
<xsl:template match="xs:element"> <!-- 输出当前元素的层级结构 --> <div class="element-node"> <span>*<xsl:value-of select="@name"/>*</span> <!-- 收集当前父元素下的所有子元素(排除嵌套在其他`xs:element`中的深层元素) --> <xsl:variable name="target-children" select=".//xs:element[ancestor::xs:complexType[parent::xs:element[generate-id() = generate-id(current())]]]"/> <!-- 按元素名称分组,仅保留每组第一个实例 --> <xsl:for-each-group select="$target-children" group-by="@name"> <xsl:apply-templates select="current-group()[1]"/> </xsl:for-each-group> </div> </xsl:template>
关键细节:
$target-children的XPath确保只收集当前父元素对应的complexType下的子元素,不会误包含更深层级的嵌套元素。- 若需处理带
@ref属性的引用元素,可将group-by扩展为group-by="if(@ref) then @ref else @name",覆盖引用场景的去重。
方法二:使用xsl:key建立唯一索引
通过键(key)绑定父元素唯一ID与子元素名称,确保每个父路径下的同名元素仅被匹配一次。
步骤1:定义全局键
<xsl:key name="unique-child-elements" match="xs:element" use="concat(generate-id(ancestor::xs:element[1]), '|', if(@ref) then @ref else @name)"/>
步骤2:在模板中调用键去重
<xsl:template match="xs:element"> <div class="element-node"> <h3>*<xsl:value-of select="@name"/>*</h3> <!-- 遍历当前父元素下的所有子元素,仅处理每个名称的第一个实例 --> <xsl:for-each select=".//xs:element[ancestor::xs:complexType[parent::xs:element[generate-id() = generate-id(current())]]]"> <xsl:if test="generate-id() = generate-id(key('unique-child-elements', concat(generate-id(ancestor::xs:element[1]), '|', if(@ref) then @ref else @name))[1])"> <xsl:apply-templates select="."/> </xsl:if> </xsl:for-each> </div> </xsl:template>
关键细节:
- 键的
use部分通过generate-id(ancestor::xs:element[1])绑定父元素的唯一标识,严格限定去重范围在当前父路径内。 - 同时处理
@name和@ref,避免引用元素重复显示。
为什么之前的方法可能失效?
你尝试的xsl:accumulator或状态传递方案,需要精准跟踪当前路径的上下文状态,若XPath路径未正确过滤深层嵌套元素,或状态更新时机有误,就会导致去重失败。而上述两种方法直接在当前父元素的上下文范围内处理,逻辑更直观,不易出错。
针对MusicXML中note元素下chord重复的场景,使用以上任一方法,都能确保chord仅在note的子层级中显示一次。
内容的提问来源于stack exchange,提问作者infojunkie
相关产品推荐
相关产品推荐

