将父节点复制到根以创建多个项目

Copying the parents nodes upto the root to create multiple items

我是 XSLT 的新手,我正在处理一个棘手的 xml 转换需求。 我的来源 xml 如下所述。

<Level_0>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>1</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>2</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>3</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
</Level_0>

要求 - 我需要让每个项目的父项达到 level_1,这意味着输出应该如下所述。基本上,应该为 level_1.

中的每个项目重复所有内容
<Level_0>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>1</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>2</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
    <Level_1>
        <Level_2>
            <Level_3>hello</Level_3>
            <Level_4>
                <Header>
                    <Value>SomeValue</Value>
                </Header>
                <SalesInvoice>
                    <Item>
                        <Detail>
                            <Position>3</Position>
                        </Detail>
                    </Item>
                </SalesInvoice>
            </Level_4>
        </Level_2>
    </Level_1>
</Level_0>


XSLT 我试过了。但是我得到了奇怪的输出。

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


    <xsl:template match="SalesInvoice">
        <NewInvoice>
            <xsl:copy-of select="../../../../Level_1/descendant::*[not(name()='SalesInvoice' or ancestor::SalesInvoice)]"/>
            <xsl:copy-of select="."/>
        </NewInvoice>
    </xsl:template>
</xsl:stylesheet>

这是处理它的一种方法。对于每个 SalesInvoice 进程,apply-templates 从它的 Level_1 开始,但将 SalesInvoice 作为参数传递(我使用 @tunnel,但您可以明确定义参数并在每个模板的每个级别传递它)。在匹配 SalesInvoice 的模板中,仅复制 $invoice-context 的内容,否则将被丢弃。

<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="xml" version="1.0" indent="yes"/>
    
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="Level_0">
        <xsl:copy>
            <xsl:for-each select="//SalesInvoice">
                <xsl:apply-templates select="ancestor::Level_1" mode="repeat">
                    <xsl:with-param name="invoice-context" select="." tunnel="yes"/>
                </xsl:apply-templates>
            </xsl:for-each>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*" mode="repeat">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" mode="repeat"/>    
        </xsl:copy>
    </xsl:template>
    
    <xsl:template match="SalesInvoice" mode="repeat">
        <xsl:param name="invoice-context" tunnel="yes"/>
        <xsl:if test="generate-id(.) eq generate-id($invoice-context)">
            <xsl:copy>
                <xsl:apply-templates select="@*|node()"/>
            </xsl:copy>
        </xsl:if>
    </xsl:template>

</xsl:stylesheet>