XSLT 1.0 - 限制每个文件的记录数

时间:2017-08-31 13:49:00

标签: xml xslt visual-studio-2013

我正在使用XSLT转换来生成XML文档,该文档在创建后将被拆分以生成多个文件。最终文件的一般结构如下:

<DOC>
    <NEWFILE/>
    <REALROOT>
        <SENDER></SENDER>
        <INVOICE>
            <IVC_ID></IVS_ID>
            <LINEITEM></LINEITEM>
            <LINEITEM></LINEITEM>
            ...
        </INVOICE>
        <INVOICE>
            <IVC_ID></IVS_ID>
            <LINEITEM></LINEITEM>
            <LINEITEM></LINEITEM>
            ...
        </INVOICE>
        ...
    </REALROOT>

    <NEWFILE/>

    <REALROOT>
        <SENDER></SENDER>
        <INVOICE>
            <IVC_ID></IVS_ID>
            <LINEITEM></LINEITEM>
            <LINEITEM></LINEITEM>
            ...
        </INVOICE>
        <INVOICE>
            <IVC_ID></IVS_ID>
            <LINEITEM></LINEITEM>
            <LINEITEM></LINEITEM>
            ...
        </INVOICE>
        ...
    </REALROOT>
    <NEWFILE/>
</DOC>

DOC<NEWFILE/>标签是最终产品的边界,但这不是问题的主题。

在输出到上述结构之前,我的输入数据需要按发件人和发票分组,我已设法做到这一点,但我还想限制每个文件的发票数量而无法找到一种方法。

我的输入数据会细分为订单项级别,如下所示:

<ROOT>
    <DATA>
        <SNDR>1</SNDR>
        <INVOICE>1</INVOICE>
        <LINEITEM>1</LINEITEM>
    </DATA>
    <DATA>
        <SNDR>1</SNDR>
        <INVOICE>1</INVOICE>
        <LINEITEM>2</LINEITEM>
    </DATA>
    <DATA>
        <SNDR>1</SNDR>
        <INVOICE>2</INVOICE>
        <LINEITEM>1</LINEITEM>
    </DATA>
    <DATA>
        <SNDR>1</SNDR>
        <INVOICE>3</INVOICE>
        <LINEITEM>1</LINEITEM>
    </DATA>
    <DATA>
        <SNDR>1</SNDR>
        <INVOICE>3</INVOICE>
        <LINEITEM>2</LINEITEM>
    </DATA>
    <DATA>
        <SNDR>2</SNDR>
        <INVOICE>1</INVOICE>
        <LINEITEM>1</LINEITEM>
    </DATA>
    <DATA>
        <SNDR>2</SNDR>
        <INVOICE>2</INVOICE>
        <LINEITEM>1</LINEITEM>
    </DATA>
</ROOT>

如果我将每个文件限制为2个发票,我希望这个输出:

<DOC>
    <REALROOT>
        <SENDER>1</SENDER>
        <INVOICE>
            <IVC_ID>1</IVS_ID>
            <LINEITEM>1</LINEITEM>
            <LINEITEM>2</LINEITEM>
        </INVOICE>
        <INVOICE>
            <IVC_ID>2</IVS_ID>
            <LINEITEM>1</LINEITEM>
        </INVOICE>
    </REALROOT>

    <NEWFILE/>

    <REALROOT>
        <SENDER>1</SENDER>
        <INVOICE>
            <IVC_ID>3</IVS_ID>
            <LINEITEM>1</LINEITEM>
            <LINEITEM>2</LINEITEM>
        </INVOICE>
    </REALROOT>

    <NEWFILE/>

    <REALROOT>
        <SENDER>2</SENDER>
        <INVOICE>
            <IVC_ID>1</IVS_ID>
            <LINEITEM>1</LINEITEM>
        </INVOICE>
        <INVOICE>
            <IVC_ID>2</IVS_ID>
            <LINEITEM>1</LINEITEM>
        </INVOICE>
    </REALROOT>
</DOC>

这是我的目标。这允许我按发件人和发票对行进行分组,但我无法弄清楚如何处理发票数量限制。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:key name="data-by-SNDR" match="/ROOT/DATA" use="SNDR"/>
<xsl:key name="data-by-invoice" match="/ROOT/DATA" use="concat(SNDR, INVOICE)"/>

<xsl:template match="/ROOT">
    <DOC>
        <newFile/>
        <xsl:for-each select="DATA[not(SNDR = preceding-sibling::DATA[1]/SNDR)]">
            <REALROOT>
                <SNDR>
                    <xsl:value-of select="SNDR"/>
                </SNDR>
                <xsl:for-each select="key('data-by-SNDR', current()/SNDR)[not(INVOICE = preceding-sibling::DATA[1]/INVOICE)]">
                    <INVOICE>
                        <IVC_ID>
                            <xsl:value-of select="INVOICE"/>
                        </IVC_ID>
                        <xsl:for-each select="key('data-by-invoice',concat(current()/SNDR, current()/INVOICE))">
                            <LINEITEM>
                                <xsl:value-of select="LINEITEM"/>
                            </LINEITEM>
                        </xsl:for-each>
                    </INVOICE>
                </xsl:for-each>
            </REALROOT>
            <newFile/>
        </xsl:for-each>
    </DOC>
</xsl:template>
</xsl:stylesheet>

1 个答案:

答案 0 :(得分:0)

首先,对于每个发件人,您需要一份此发件人的所有发票清单。

这已经是你在前两个foreach所做的事了,但我说你需要一个清单。所以第二个,不需要是一个foreach。把它们放在一个变量中:

<xsl:variable name="invoicesWithThisSender"
                    select="key('data-by-SNDR', current()/SNDR)[not(INVOICE = preceding-sibling::DATA[1]/INVOICE)]" />

它是一个变量,而不是for-each。

现在你需要在尽可能多的文件中写下这个列表的所有元素。

由于XSLT 1.0不提供分区机制,我知道这样做的唯一方法是调用递归命名模板。

试试这个:

<xsl:template name="writeFilesForSender">
    <xsl:param name="invoicesWithThisSender" />

    <xsl:if test="$invoicesWithThisSender">
        <NEWFILE />

        <REALROOT>
            <SNDR>
                <xsl:value-of select="SNDR" />
            </SNDR>
            <xsl:for-each select="$invoicesWithThisSender[position() &lt;= 2]">
                <INVOICE>
                    <IVC_ID>
                        <xsl:value-of select="INVOICE" />
                    </IVC_ID>
                </INVOICE>
            </xsl:for-each>
        </REALROOT>

        <xsl:call-template name="writeFilesForSender">
            <xsl:with-param name="invoicesWithThisSender"
                select="$invoicesWithThisSender[position() > 2]" />
        </xsl:call-template>
    </xsl:if>

</xsl:template>

用它来称呼它:

<xsl:call-template name="writeFilesForSender">
    <xsl:with-param name="invoicesWithThisSender"
        select="$invoicesWithThisSender" />
</xsl:call-template>

根据我的理解和测试,你的问题就解决了。