首页 > 解决方案 > XSLT - 使用最后设置的标题值将多个相同类型的标签转换为块

问题描述

我想在标题之后和标题名称包围的下一个标题之前获取所有元素。

我有一个看起来像这样的 Xml:

<Main>
  <Sec>
    <Header>A</Header>
    <Body>A1</Body>
  </Sec>
  <Sec>
    <Body>A2</Body>
  </Sec>
  <Sec>
    <Header>B</Header>
    <Body>B1</Body>
  </Sec>
  <Sec>
    <Body>B2</Body>
  </Sec>
  <Sec>
    <Body>B3</Body>
  </Sec>
</Main>

我想将上面的 XML 转换成这种格式:

<Main>
  <Sec>
    <Type1>A1</Type1>
    <Type1>A2</Type1>
    <Type2>B1</Type2>
    <Type2>B2</Type2>
    <Type2>B3</Type2>
  </Sec>
</Main>

Type1 和 Type2 是固定的,所以我们不需要从标头标签中获取标签名称。

我是 Xml 的新手,不知道如何使用基本控件来做到这一点。提前致谢。

标签: xmlxslt

解决方案


以下XSLT 2.0解决方案用于xsl:for-each-group选择所有Body元素并Header沿preceding::轴按第一个元素对它们进行分组。

对于这些Body元素组中的每一个,它使用该Header值构造“类型”元素的名称。我曾经string-to-codepoints()将字母转换为它们的代码点编号 65AB66,然后减去 1。您可以使用查找,或一组 if/else,或者xsl:choose如果魔术公式令人困惑或您的实际数据不允许简单的计算。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
  <xsl:output indent="yes"/>
    
  <xsl:template match="Main">
    <xsl:copy>
      <Sec>
        <xsl:for-each-group group-by="preceding::Header[1]" select=".//Body">
          <xsl:for-each select="current-group()">
            <xsl:element name="type{string-to-codepoints(current-grouping-key()) - 64}">
              <xsl:apply-templates select="."/>
            </xsl:element>
          </xsl:for-each>
        </xsl:for-each-group>
      </Sec>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

XSLT 1.0解决方案还生成所需的输出。它不使用string-to-codepoints(),而是使用translate()函数转换A1B转换为2

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output indent="yes"/>
    
  <xsl:template match="Main">
    <xsl:copy>
      <Sec>
        <xsl:apply-templates select="//Body"/>
      </Sec>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Body">       
    <xsl:element name="type{translate(preceding::Header[1], 'AB','12')}">
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
    
</xsl:stylesheet>

推荐阅读