首页 > 解决方案 > XSLT - 如何在新父节点中对一组递归节点进行分组

问题描述

我开始使用 XSLT,在探索了一些解决方案之后,我认为与您分享我的问题是个好主意。也许有人可以帮助我。

我有一个这样的 XML 输入数据:

<?xml version="1.0" encoding="windows-1252"?>
<DOCUMENT>
<SSDP>
    <DP>A</DP>
    <DX>B</DX>
    <XN>1</XN>
    <PP>2</PP>
    <CO>3</CO>
    <XN>4</XN>
    <PP>5</PP>
    <XN>6</XN>
    <PP>7</PP>
    <CO>8</CO>
</SSDP>
<SSDP>
    <DP>A</DP>
    <DL>B</DL>
    <DX>C</DX>
    <XN>1</XN>
    <PP>2</PP>
    <CO>3</CO>
    <XN>4</XN>
    <PP>5</PP>
</SSDP>
</DOCUMENT>

如您所见,有一组递归节点。具体来说,每个“XN”节点开始一组新的递归节点,直到下一个“XN”字段。(“XN”节点之间的字段数可以是动态的,如您在输入 XML 示例中所见)。挑战是在新节点“DATA”中包含每组递归节点。

想要的输出如下:

<?xml version="1.0" encoding="windows-1252"?>
<DOCUMENT>
<SSDP>
    <DP>A</DP>
    <DX>B</DX>
    <DATA>
        <XN>1</XN>
        <PP>2</PP>
        <CO>3</CO>
    </DATA>
    <DATA>
        <XN>4</XN>
        <PP>5</PP>
    </DATA>
    <DATA>
        <XN>6</XN>
        <PP>7</PP>
        <CO>8</CO>
    </DATA>
</SSDP>
<SSDP>
    <DP>A</DP>
    <DL>B</DL>
    <DX>C</DX>
    <DATA>
        <XN>1</XN>
        <PP>2</PP>
        <CO>3</CO>
    </DATA>
    <DATA>
        <XN>4</XN>
        <PP>5</PP>
    </DATA>
</SSDP>
</DOCUMENT>

正如我之前所说,我是 XSLT 的初学者,我试图创建一个来解决这个问题,但恐怕我还没有足够的知识。

我的第一个 XSLT 版本如下:

<?xml version="1.0" encoding="windows-1252"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output indent="yes"/>
    <xsl:template match="DOCUMENT">
        <DOCUMENT>
        <xsl:for-each select="SSDP">
            <SSDP>
            <xsl:for-each select="*">
                <!-- First element that define a DATA node -->
                <xsl:if test="name(.)='XN'">
                    <xsl:element name="DATA">                   
                        <xsl:copy-of select="."/>
                    </xsl:element>
                </xsl:if>
                <!-- List of elements that define a DATA node -->
                <xsl:if test="name(.)!='XN' and
                              name(.)!='PP' and
                              name(.)!='CO'">
                    <xsl:copy-of select="."/>
                </xsl:if>
            </xsl:for-each>
            </SSDP>
        </xsl:for-each>
        </DOCUMENT>
    </xsl:template>
</xsl:stylesheet>

但结果是不完整的...

<?xml version="1.0" encoding="UTF-8"?>
<DOCUMENT>
   <SSDP>
      <DP>A</DP>
      <DX>B</DX>
      <DATA>
         <XN>1</XN>
      </DATA>
      <DATA>
         <XN>4</XN>
      </DATA>
      <DATA>
         <XN>6</XN>
      </DATA>
   </SSDP>
   <SSDP>
      <DP>A</DP>
      <DL>B</DL>
      <DX>C</DX>
      <DATA>
         <XN>1</XN>
      </DATA>
      <DATA>
         <XN>4</XN>
      </DATA>
   </SSDP>
</DOCUMENT>

我认为在包含“DATA”节点并将鼠标悬停在“XN”节点上之后,我需要添加以下“PP”或“CO”节点,使用“following::”或类似的功能......但我没有不知道怎么做。

另外,我担心有比我想象的更好的方法来解决这个问题。

提前致谢!

标签: xmlxsltgroupingnodesrecursive-datastructures

解决方案


另外,我担心有比我想象的更好的方法来解决这个问题。

我相信有:

XSLT 2.0

<xsl:stylesheet version="2.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="utf-8" indent="yes"/>

<xsl:template match="/DOCUMENT">
    <xsl:copy>
        <xsl:for-each select="SSDP">
            <xsl:copy>
                <xsl:for-each-group select="*" group-starting-with="XN">
                    <xsl:choose>
                        <xsl:when test="self::XN">
                            <DATA>
                                <xsl:copy-of select="current-group()" />
                            </DATA>
                        </xsl:when>
                        <xsl:otherwise>
                            <xsl:copy-of select="current-group()" />
                        </xsl:otherwise>
                    </xsl:choose>
                </xsl:for-each-group>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

推荐阅读