xml - 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 的新手,不知道如何使用基本控件来做到这一点。提前致谢。
解决方案
以下XSLT 2.0解决方案用于xsl:for-each-group
选择所有Body
元素并Header
沿preceding::
轴按第一个元素对它们进行分组。
对于这些Body
元素组中的每一个,它使用该Header
值构造“类型”元素的名称。我曾经string-to-codepoints()
将字母转换为它们的代码点编号 65A
和B
66,然后减去 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()
函数转换A
为1
和B
转换为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>