按部分值对子元素进行分组

发布于 2024-11-01 15:01:22 字数 1243 浏览 1 评论 0原文

我有这个 XML 文件:

<Elements>
  <Element name="A.B.C.x">
    <Child>...</Child>
    <Child>...</Child>
    <Child>...</Child>
  </Element>
  <Element name="A.B.C.y">
    <Child>...</Child>
    <Child>...</Child>
  </Element>
  <Element name="A.D.E.y">
    <Child>...</Child>
  </Element>
  <Element name="A.D.E.z">
    <Child>...</Child>
    <Child>...</Child>
    <Child>...</Child>
  </Element>
</Elements>

我需要创建 XSL 才能获得此结果:

<Elements>
  <Element name="A.B.C">
    <LastToken name="x" childCount="3" />
    <LastToken name="y" childCount="2" />
  </Element>
  <Element name="A.D.E">
    <LastToken name="y" childCount="1" />
    <LastToken name="z" childCount="3" />
  </Element>
</Elements>

我仅限于没有扩展的 XSL 1.0,并且我不知道如何实现该结果。

任何帮助表示赞赏。
提前致谢。

编辑:随着一些答案的出现,我看到我必须澄清我的问题/任务:
Element 节点的 name 属性中的标记不限于一个字符。 “name”属性的示例值可以是 This.Is.Grouping.Target.AndThisIsGroupChild

I have this XML file:

<Elements>
  <Element name="A.B.C.x">
    <Child>...</Child>
    <Child>...</Child>
    <Child>...</Child>
  </Element>
  <Element name="A.B.C.y">
    <Child>...</Child>
    <Child>...</Child>
  </Element>
  <Element name="A.D.E.y">
    <Child>...</Child>
  </Element>
  <Element name="A.D.E.z">
    <Child>...</Child>
    <Child>...</Child>
    <Child>...</Child>
  </Element>
</Elements>

I need to create XSL to get this result:

<Elements>
  <Element name="A.B.C">
    <LastToken name="x" childCount="3" />
    <LastToken name="y" childCount="2" />
  </Element>
  <Element name="A.D.E">
    <LastToken name="y" childCount="1" />
    <LastToken name="z" childCount="3" />
  </Element>
</Elements>

I am limited to XSL 1.0 with no extensions and I cannot figure out how to achieve the result.

Any help appreciated.
Thanks in advance.

EDIT: As some answers came in I saw I have to clarify my question/task:
Tokens in name attribute of Element node are not limited to one character. Sample value of 'name' attribute could be This.Is.Grouping.Target.AndThisIsGroupChild

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

若无相欠,怎会相见 2024-11-08 15:01:22

此 XSLT 1.0 转换(绝对没有限制):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kElemByName" match="Element"
          use="@name"/>

 <xsl:key name="klastTokenByName" match="@lastToken"
  use="../@name"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="Element/@name">
  <xsl:attribute name="name">
   <xsl:call-template name="init"/>
  </xsl:attribute>
  <xsl:attribute name="lastToken">
   <xsl:call-template name="lastToken"/>
  </xsl:attribute>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <xsl:apply-templates/>
  </xsl:variable>
  <xsl:apply-templates mode="pass2"
      select="ext:node-set($vrtfPass1)/*"/>
 </xsl:template>

 <xsl:template mode="pass2" match="Element"/>

 <xsl:template mode="pass2" match=
  "Element[generate-id()
          =
           generate-id(key('kElemByName',@name)[1])
          ]
  ">
  <Element name="{@name}">
    <xsl:for-each select=
    "key('klastTokenByName',@name)">

     <lastToken name="{.}"
       childCount="{count(key('kElemByName',../@name)
                               [@lastToken=current()]
                                 /Child
                          )
                    }"
     />
    </xsl:for-each>
  </Element>
 </xsl:template>

 <xsl:template name="lastToken">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pDelim" select="'.'"/>

  <xsl:variable name="vrtfTokens">
   <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select="$pText"/>
    <xsl:with-param name="pDelim" select="$pDelim"/>
   </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select=
   "ext:node-set($vrtfTokens)/*[last()]"/>
 </xsl:template>

 <xsl:template name="init">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pDelim" select="'.'"/>

  <xsl:variable name="vLastToken">
    <xsl:call-template name="lastToken">
     <xsl:with-param name="pText" select="$pText"/>
     <xsl:with-param name="pDelim" select="$pDelim"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select=
   "substring($pText,
              1,
               string-length($pText)
              - string-length($vLastToken)
              - string-length($pDelim)
              )
   "/>
 </xsl:template>

 <xsl:template name="tokenize">
  <xsl:param name="pText"/>
  <xsl:param name="pDelim" select="'.'"/>

  <xsl:if test="string-length($pText)">
    <token>
     <xsl:value-of select=
      "substring-before(concat($pText,$pDelim),
                        $pDelim)"/>
    </token>
    <xsl:call-template name="tokenize">
     <xsl:with-param name="pText" select=
     "substring-after($pText,$pDelim)"/>
    </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

应用于提供的 XML 文档时

<Elements>
    <Element name="A.B.C.x">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.B.C.y">
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.y">
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.z">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
</Elements>

生成所需的正确结果:

<Elements>
   <Element name="A.B.C">
      <lastToken name="x" childCount="3"/>
      <lastToken name="y" childCount="2"/>
   </Element>
   <Element name="A.D.E">
      <lastToken name="y" childCount="1"/>
      <lastToken name="z" childCount="3"/>
   </Element>
</Elements>

This XSLT 1.0 transformation (absolutely no limitations):

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:ext="http://exslt.org/common"
 exclude-result-prefixes="ext">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:key name="kElemByName" match="Element"
          use="@name"/>

 <xsl:key name="klastTokenByName" match="@lastToken"
  use="../@name"/>

 <xsl:template match="node()|@*">
  <xsl:copy>
   <xsl:apply-templates select="node()|@*"/>
  </xsl:copy>
 </xsl:template>

 <xsl:template match="Element/@name">
  <xsl:attribute name="name">
   <xsl:call-template name="init"/>
  </xsl:attribute>
  <xsl:attribute name="lastToken">
   <xsl:call-template name="lastToken"/>
  </xsl:attribute>
 </xsl:template>

 <xsl:template match="/">
  <xsl:variable name="vrtfPass1">
   <xsl:apply-templates/>
  </xsl:variable>
  <xsl:apply-templates mode="pass2"
      select="ext:node-set($vrtfPass1)/*"/>
 </xsl:template>

 <xsl:template mode="pass2" match="Element"/>

 <xsl:template mode="pass2" match=
  "Element[generate-id()
          =
           generate-id(key('kElemByName',@name)[1])
          ]
  ">
  <Element name="{@name}">
    <xsl:for-each select=
    "key('klastTokenByName',@name)">

     <lastToken name="{.}"
       childCount="{count(key('kElemByName',../@name)
                               [@lastToken=current()]
                                 /Child
                          )
                    }"
     />
    </xsl:for-each>
  </Element>
 </xsl:template>

 <xsl:template name="lastToken">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pDelim" select="'.'"/>

  <xsl:variable name="vrtfTokens">
   <xsl:call-template name="tokenize">
    <xsl:with-param name="pText" select="$pText"/>
    <xsl:with-param name="pDelim" select="$pDelim"/>
   </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select=
   "ext:node-set($vrtfTokens)/*[last()]"/>
 </xsl:template>

 <xsl:template name="init">
  <xsl:param name="pText" select="."/>
  <xsl:param name="pDelim" select="'.'"/>

  <xsl:variable name="vLastToken">
    <xsl:call-template name="lastToken">
     <xsl:with-param name="pText" select="$pText"/>
     <xsl:with-param name="pDelim" select="$pDelim"/>
    </xsl:call-template>
  </xsl:variable>

  <xsl:value-of select=
   "substring($pText,
              1,
               string-length($pText)
              - string-length($vLastToken)
              - string-length($pDelim)
              )
   "/>
 </xsl:template>

 <xsl:template name="tokenize">
  <xsl:param name="pText"/>
  <xsl:param name="pDelim" select="'.'"/>

  <xsl:if test="string-length($pText)">
    <token>
     <xsl:value-of select=
      "substring-before(concat($pText,$pDelim),
                        $pDelim)"/>
    </token>
    <xsl:call-template name="tokenize">
     <xsl:with-param name="pText" select=
     "substring-after($pText,$pDelim)"/>
    </xsl:call-template>
  </xsl:if>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<Elements>
    <Element name="A.B.C.x">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.B.C.y">
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.y">
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.z">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
</Elements>

the wanted, correct result is produced:

<Elements>
   <Element name="A.B.C">
      <lastToken name="x" childCount="3"/>
      <lastToken name="y" childCount="2"/>
   </Element>
   <Element name="A.D.E">
      <lastToken name="y" childCount="1"/>
      <lastToken name="z" childCount="3"/>
   </Element>
</Elements>
渔村楼浪 2024-11-08 15:01:22

只是为了好玩,没有扩展的通用 XSLT 1.0 解决方案:

<!DOCTYPE xsl:stylesheet [
  <!ENTITY key "
substring(
   @name,
   1,
   string-length(
      @name
   )
 - count(
      document('')//node()[
         not(
            contains(
               substring(
                  current()/@name,
                  string-length(
                     current()/@name
                  )
                - position()
                + 1
               ),
               '.'
            )
         )
      ]
   )
 - 1
)">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kElementByNamePrefix" match="Element" use="&key;"/>
    <xsl:key name="kElementByName" match="Element" use="@name"/>
    <xsl:template match="Element">
        <xsl:variable name="vNamePrefix" select="&key;"/>
        <xsl:variable name="vCurrentGroup"
         select="key('kElementByNamePrefix',$vNamePrefix)"/>
        <xsl:if test="generate-id() = generate-id($vCurrentGroup[1])">
            <Element name="{$vNamePrefix}">
                <xsl:apply-templates
                 select="$vCurrentGroup[
                            generate-id()
                          = generate-id(
                               key('kElementByName',@name)[1]
                            )
                         ]"
                 mode="prefix">
                    <xsl:with-param name="pNamePrefix" select="$vNamePrefix"/>
                </xsl:apply-templates>
            </Element>
        </xsl:if>
    </xsl:template>
    <xsl:template match="Element" mode="prefix">
        <xsl:param name="pNamePrefix"/>
        <LastToken name="{substring(substring-after(@name,$pNamePrefix),2)}"
                   childCount="{count(key('kElementByName',@name)/Child)}"/>
    </xsl:template>
</xsl:stylesheet>

输出:

<Element name="A.B.C">
    <LastToken name="x" childCount="3" />
    <LastToken name="y" childCount="2" />
</Element>
<Element name="A.D.E">
    <LastToken name="y" childCount="1" />
    <LastToken name="z" childCount="3" />
</Element>

Just for fun, a general XSLT 1.0 solution without extensions:

<!DOCTYPE xsl:stylesheet [
  <!ENTITY key "
substring(
   @name,
   1,
   string-length(
      @name
   )
 - count(
      document('')//node()[
         not(
            contains(
               substring(
                  current()/@name,
                  string-length(
                     current()/@name
                  )
                - position()
                + 1
               ),
               '.'
            )
         )
      ]
   )
 - 1
)">
]>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:key name="kElementByNamePrefix" match="Element" use="&key;"/>
    <xsl:key name="kElementByName" match="Element" use="@name"/>
    <xsl:template match="Element">
        <xsl:variable name="vNamePrefix" select="&key;"/>
        <xsl:variable name="vCurrentGroup"
         select="key('kElementByNamePrefix',$vNamePrefix)"/>
        <xsl:if test="generate-id() = generate-id($vCurrentGroup[1])">
            <Element name="{$vNamePrefix}">
                <xsl:apply-templates
                 select="$vCurrentGroup[
                            generate-id()
                          = generate-id(
                               key('kElementByName',@name)[1]
                            )
                         ]"
                 mode="prefix">
                    <xsl:with-param name="pNamePrefix" select="$vNamePrefix"/>
                </xsl:apply-templates>
            </Element>
        </xsl:if>
    </xsl:template>
    <xsl:template match="Element" mode="prefix">
        <xsl:param name="pNamePrefix"/>
        <LastToken name="{substring(substring-after(@name,$pNamePrefix),2)}"
                   childCount="{count(key('kElementByName',@name)/Child)}"/>
    </xsl:template>
</xsl:stylesheet>

Output:

<Element name="A.B.C">
    <LastToken name="x" childCount="3" />
    <LastToken name="y" childCount="2" />
</Element>
<Element name="A.D.E">
    <LastToken name="y" childCount="1" />
    <LastToken name="z" childCount="3" />
</Element>
思念满溢 2024-11-08 15:01:22

XSLT 2.0 解决方案(绝对没有任何限制):

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my:my" exclude-result-prefixes="xs my"
    >
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:for-each-group select="/*/*"
        group-by="substring(@name, 1,
                             string-length(@name)
                            - string-length(my:LastToken(@name)) -1)">
      <xsl:variable name="vLastToken"
           select="my:LastToken(@name)"/>
      <Element name="{substring(@name,1,
                                  string-length(@name)
                                 -
                                   string-length($vLastToken)-1)}">

       <xsl:for-each-group select="current-group()"
        group-by="my:LastToken(@name)">

        <xsl:variable name="vLastToken" select="my:LastToken(@name)"/>

        <LastToken name="{$vLastToken}"
                   childCount="{count(current-group()/Child)}"/>
       </xsl:for-each-group>

      </Element>
     </xsl:for-each-group>
 </xsl:template>

 <xsl:function name="my:LastToken" as="xs:string">
  <xsl:param name="pText" as="xs:string"/>

  <xsl:sequence select="tokenize($pText, '\.')[last()]"/>
 </xsl:function>
</xsl:stylesheet>

应用于提供的 XML 文档时:

<Elements>
    <Element name="A.B.C.x">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.B.C.y">
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.y">
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.z">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
</Elements>

生成所需的正确结果:

<Element name="A.B.C">
   <LastToken name="x" childCount="3"/>
   <LastToken name="y" childCount="2"/>
</Element>
<Element name="A.D.E">
   <LastToken name="y" childCount="1"/>
   <LastToken name="z" childCount="3"/>
</Element>

An XSLT 2.0 solution (absolutely no limitations assumed):

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    xmlns:my="my:my" exclude-result-prefixes="xs my"
    >
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

 <xsl:template match="/">
     <xsl:for-each-group select="/*/*"
        group-by="substring(@name, 1,
                             string-length(@name)
                            - string-length(my:LastToken(@name)) -1)">
      <xsl:variable name="vLastToken"
           select="my:LastToken(@name)"/>
      <Element name="{substring(@name,1,
                                  string-length(@name)
                                 -
                                   string-length($vLastToken)-1)}">

       <xsl:for-each-group select="current-group()"
        group-by="my:LastToken(@name)">

        <xsl:variable name="vLastToken" select="my:LastToken(@name)"/>

        <LastToken name="{$vLastToken}"
                   childCount="{count(current-group()/Child)}"/>
       </xsl:for-each-group>

      </Element>
     </xsl:for-each-group>
 </xsl:template>

 <xsl:function name="my:LastToken" as="xs:string">
  <xsl:param name="pText" as="xs:string"/>

  <xsl:sequence select="tokenize($pText, '\.')[last()]"/>
 </xsl:function>
</xsl:stylesheet>

when applied on the provided XML document:

<Elements>
    <Element name="A.B.C.x">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.B.C.y">
        <Child>...</Child>
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.y">
        <Child>...</Child>
    </Element>
    <Element name="A.D.E.z">
        <Child>...</Child>
        <Child>...</Child>
        <Child>...</Child>
    </Element>
</Elements>

the wanted, correct result is produced:

<Element name="A.B.C">
   <LastToken name="x" childCount="3"/>
   <LastToken name="y" childCount="2"/>
</Element>
<Element name="A.D.E">
   <LastToken name="y" childCount="1"/>
   <LastToken name="z" childCount="3"/>
</Element>
苍暮颜 2024-11-08 15:01:22

使用慕尼黑分组

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="k1" match="Element" use="substring(@name, 1, 5)"/>

<xsl:key name="k2" match="Element" use="@name"/>

<xsl:template match="Elements">
  <xsl:copy>
    <xsl:apply-templates select="Element[generate-id() = generate-id(key('k1', substring(@name, 1, 5))[1])]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Element">
  <Element name="{substring(@name, 1, 5)}">
    <xsl:apply-templates select="key('k1', substring(@name, 1, 5))[generate-id() = generate-id(key('k2', @name)[1])]" mode="token">
      <xsl:sort select="substring(@name, 7)"/>
    </xsl:apply-templates>
  </Element>
</xsl:template>

<xsl:template match="Element" mode="token">
  <LastToken name="{substring(@name, 7)}" childCount="{count(key('k2', @name)/Child)}"/>
</xsl:template>

</xsl:stylesheet>

Use Muenchian grouping:

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

<xsl:output method="xml" indent="yes"/>
<xsl:strip-space elements="*"/>

<xsl:key name="k1" match="Element" use="substring(@name, 1, 5)"/>

<xsl:key name="k2" match="Element" use="@name"/>

<xsl:template match="Elements">
  <xsl:copy>
    <xsl:apply-templates select="Element[generate-id() = generate-id(key('k1', substring(@name, 1, 5))[1])]"/>
  </xsl:copy>
</xsl:template>

<xsl:template match="Element">
  <Element name="{substring(@name, 1, 5)}">
    <xsl:apply-templates select="key('k1', substring(@name, 1, 5))[generate-id() = generate-id(key('k2', @name)[1])]" mode="token">
      <xsl:sort select="substring(@name, 7)"/>
    </xsl:apply-templates>
  </Element>
</xsl:template>

<xsl:template match="Element" mode="token">
  <LastToken name="{substring(@name, 7)}" childCount="{count(key('k2', @name)/Child)}"/>
</xsl:template>

</xsl:stylesheet>
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文