首页 > 解决方案 > xslt 通过子字符串删除重复值

问题描述

我有以下类别:

<categories>
    <category>anotherparent</category>
    <category>parent</category>
    <category>parent/child1</category>
    <category>parent/child1/subchild1</category>
    <category>parent/child2</category>
    <category>parent/child3/</category>
    <category>parent/child3/subchild3</category>
</categories>

这里的问题是类别路径是“重复的”。基本上我想删除所有父类别路径,只包括最具体的级别。所以结果应该是这样的:

<categories>
    <category>anotherparent</category>
    <category>parent/child1/subchild1</category>
    <category>parent/child2</category>
    <category>parent/child3/subchild3</category>
</categories>

我可以考虑一些 java 扩展,但我找不到正确的方法/函数如何在 xslt 中执行此操作,我很确定它应该很容易。

它可以是 xslt 2 或 3。

标签: xslt

解决方案


也许

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    expand-text="yes"
    exclude-result-prefixes="#all"
    xmlns:mf="http://example.com/mf"
    version="3.0">
  
  <xsl:function name="mf:group" as="element(category)*">
    <xsl:param name="cats"/>
    <xsl:param name="level"/>
    <xsl:choose>
      <xsl:when test="$cats?2[$level]">
        <xsl:for-each-group select="$cats[?2[$level]]" group-by="?2[$level]">
          <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
        </xsl:for-each-group>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$cats?1"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:function>

  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:output indent="yes"/>

  <xsl:template match="categories">
    <xsl:copy>
      <xsl:sequence select="mf:group(category ! [., tokenize(., '/')], 1)"/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>

有帮助;假设,就像评论所要求的那样,尾随/<category>parent/child3/</category>一个错字,并且是<category>parent/child3</category>. 如果parent/child3/可以发生但应该被视为parent/child3then 使用tokenize(., '/')[normalize-space()]而不是tokenize(., '/').

在函数中使用包含两个项目的映射序列而不是大小为 2 的数组序列可能更简洁:

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    expand-text="yes"
    exclude-result-prefixes="#all"
    xmlns:mf="http://example.com/mf"
    version="3.0">
  
  <xsl:function name="mf:group" as="element(category)*">
    <xsl:param name="cats" as="map(xs:string, item()*)*"/>
    <xsl:param name="level" as="xs:integer"/>
    <xsl:choose>
      <xsl:when test="$cats?tokens[$level]">
        <xsl:for-each-group select="$cats[?tokens[$level]]" group-by="?tokens[$level]">
          <xsl:sequence select="mf:group(current-group(), $level + 1)"/>
        </xsl:for-each-group>
      </xsl:when>
      <xsl:otherwise>
        <xsl:sequence select="$cats?cat"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:function>

  <xsl:mode on-no-match="shallow-copy"/>
  
  <xsl:output indent="yes"/>

  <xsl:template match="categories">
    <xsl:copy>
      <xsl:sequence select="mf:group(category ! map { 'cat' : ., 'tokens' : tokenize(., '/') }, 1)"/>
    </xsl:copy>
  </xsl:template>
  
</xsl:stylesheet>

同样,如果可能出现尾随或前导或在斜杠之间,可能需要使用tokenize(., '/')[normalize-space()]而不是,tokenize(., '/')但应该忽略。


推荐阅读