Доступ к сгруппированным узлам с помощью xpath для создания постоянного вывода

У меня есть ниже XML

<toPay>
<Pay>
    <amount>1111</amount>       
    <accountNumber>223128987</accountNumber>
    <payType>PAYROLL</payType>
</Pay>
<Pay>
    <amount>2222</amount>
    <accountNumber>123128987</accountNumber>
    <payType>PAYROLL</payType>
</Pay>
<Pay>
    <amount>333</amount>
    <accountNumber>645032</accountNumber>
    <payType>MAIN</payType>
</Pay>

Мне нужен вывод в этом формате:

<root>
<element>
    <amount>1111</amount>
    <accountNumber>223128987</accountNumber>
</element>
<element>
    <amount>2222</amount>
    <accountNumber>223128987</accountNumber>
</element>
<element>
    <amount></amount>
    <accountNumber></accountNumber>
</element>
<element>
    <amount></amount>
    <accountNumber></accountNumber>
</element>

Я написал следующий код:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:key name="groups" match="payType" use="." />

<xsl:template match="/toPay/Pay">
    <root>
        <xsl:apply-templates select="payType[generate-id() = generate-id(key('groups', .)[1])]" />
    </root>
</xsl:template>

<xsl:template match="payType">
    <xsl:variable name="currentGroup" select="." />
        <xsl:if test="$currentGroup = 'PAYROLL'">

            <xsl:for-each select="key('groups', $currentGroup)">
                <element>
                    <xsl:if test="position() = 1">
                        <amount>
                            <xsl:value-of select="../amount" />
                        </amount>
                        <accountNumber>
                            <xsl:value-of select="../accountNumber" />
                        </accountNumber>
                    </xsl:if>                   
                    <xsl:if test="position() = 2">
                        <amount>
                            <xsl:value-of select="../amount" />
                        </amount>
                        <accountNumber>
                            <xsl:value-of select="../accountNumber" />
                        </accountNumber>
                    </xsl:if>
                </element>
            </xsl:for-each>
        </xsl:if>
</xsl:template>

Но проблема, с которой я сталкиваюсь, заключается в том, что я не могу получить доступ к сгруппированным узлам для paytype -PAYROLL. Единственный способ, который я знаю, - это позиция, но мое требование всегда указывать 4 элемента-элемента всегда в выходных данных, независимо от того, существуют или нет узлы в исходном xml с PAYtype -PAYROLL. Если существует, то значения того же eeds должны быть заполнены, если нет, то теги xml без значений.

Любая помощь приветствуется - xslt 1.0 или 2.0.

2 ответа

Если я правильно понял ваше объяснение, вы могли бы сделать что-то очень простое, даже если немного примитивное:

<xsl:template match="/toPay">
    <xsl:variable name="payroll" select="Pay[payType='PAYROLL']" />
    <root>
        <element>
            <amount>
                <xsl:value-of select="$payroll[1]/amount" />
            </amount>
            <accountNumber>
                <xsl:value-of select="$payroll[1]/accountNumber" />         
            </accountNumber>
        </element>
        <element>
            <amount>
                <xsl:value-of select="$payroll[2]/amount" />
            </amount>
            <accountNumber>
                <xsl:value-of select="$payroll[2]/accountNumber" />         
            </accountNumber>
        </element>
        <element>
            <amount>
                <xsl:value-of select="$payroll[3]/amount" />
            </amount>
            <accountNumber>
                <xsl:value-of select="$payroll[3]/accountNumber" />         
            </accountNumber>
        </element>
        <element>
            <amount>
                <xsl:value-of select="$payroll[4]/amount" />
            </amount>
            <accountNumber>
                <xsl:value-of select="$payroll[4]/accountNumber" />         
            </accountNumber>
        </element>
    </root>
</xsl:template>

Я не вижу требования для группировки чего-либо.


Вот немного более элегантная версия:

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:strip-space elements="*"/>

<xsl:template match="/toPay">
    <xsl:variable name="payroll" select="Pay[payType='PAYROLL']" />
    <root>
        <xsl:apply-templates select="$payroll"/>
        <xsl:for-each select="1 to 4 - count($payroll)">
             <element>
                <amount/>
                <accountNumber/>
              </element>
        </xsl:for-each>
    </root>
</xsl:template>

<xsl:template match="Pay">
    <element>
        <xsl:copy-of select="amount, accountNumber"/>
    </element>
</xsl:template>

</xsl:stylesheet>

Демо: http://xsltransform.net/6pS1zDr

Я также не вижу необходимости в группировке, если вы хотите вывести пустые элементы для тех элементов, которых нет, тогда с XSLT 2.0 вы можете использовать

<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

    <xsl:output indent="yes"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="toPay">
        <root>
            <xsl:variable name="payroll" select="Pay[payType = 'PAYROLL']"/>
            <xsl:apply-templates select="$payroll"/>
            <xsl:for-each select="(count($payroll) + 1) to 4">
              <element>
                <amount></amount>
                <accountNumber></accountNumber>
            </element>  
            </xsl:for-each>
        </root>
    </xsl:template>

    <xsl:template match="Pay">
        <element>
            <xsl:apply-templates select="* except payType"/>
        </element>
    </xsl:template>
</xsl:transform>
Другие вопросы по тегам