Обработка нескольких условных циклов в XSLT

У меня есть пример сообщения, которое нужно преобразовать в другую структуру вывода с помощью XSLT.

Входящее сообщение

 <document>
    <ObjectId>
    <ID>1000909090</ID>
    <dlex>
        <attrGroupMany name="streetinfo">
            <row>                                              <!-- Mandatory Loop -->
                <attr name="streetcode">AS_DRAINED</attr> 
                <attrQualMany name="streetintake">             <!-- Optional Loop -->
                    <value qual="en">dvif1</value>
                    <value qual="nl">dvif2</value>
                </attrQualMany>
                <attr name="streettype">BY_MEASURE</attr>
                <attrQual name="streetbasis" qual="ONZ">5</attrQual>
                <attrQual name="streetsize" qual="EA">1</attrQual>
                <attrQualMany name="streetsizeDescription">    <!-- Optional Loop -->
                    <value qual="en">sz1</value>
                    <value qual="hi">sz2</value>
                </attrQualMany>
                <attrGroupMany name="streetDetails">
                    <row>                                     <!-- Optional Loop -->
                        <attr name="streetTypeCode">FAT</attr>
                        <attr name="streetValueIntakePercent">25</attr>
                        <attr name="streetPrecisionCode">APPROXIMATELY</attr>
                        <attrQualMany name="streetContained">   <!-- Optional Loop -->
                            <value qual="ONZ">2</value>
                            <value qual="OZA">3</value>
                        </attrQualMany>
                    </row>
                    <row>
                        <attr name="streetTypeCode">FAMS</attr>
                        <attr name="streetValueIntakePercent">999</attr>
                        <attr name="streetPrecisionCode">EXACT</attr>
                        <attrQualMany name="streetContained">
                            <value qual="ONZ">4</value>
                            <value qual="OZA">5</value>
                        </attrQualMany>
                    </row>
                </attrGroupMany>
            </row>
        </attrGroupMany>
    </dlex>
</ObjectId>
</document>

Выходное сообщение

<?xml version="1.0" encoding="UTF-8"?>
<CatalogObjectId>
<RelationshipData>
  <Relationship>
     <RelationType>ObjectId_Street</RelationType>
     <RelatedObjectIds>
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz1-en-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz1-en-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz1-en-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz1-en-FAMS-5-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz2-hi-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz2-hi-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz2-hi-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif1-en-sz2-hi-FAMS-5-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz1-en-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz1-en-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz1-en-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz1-en-FAMS-5-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz2-hi-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz2-hi-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz2-hi-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED-dvif2-nl-sz2-hi-FAMS-5-OZA" />
     </RelatedObjectIds>
  </Relationship>
</RelationshipData>
</CatalogObjectId>

Это отлично работает, когда мы используем приведенный ниже XSLT.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="xml" indent="yes" />
<xsl:template match="document">
    <CatalogObjectId>
        <RelationshipData>
            <Relationship>
                <RelationType>ObjectId_Street</RelationType>
                <RelatedObjectIds>
                    <xsl:for-each select="ObjectId/dlex/attrGroupMany[@name='streetinfo']/row">  
                        <xsl:variable name="v_position_streetinfo" select="position()" />                                                   
                        <xsl:variable name="v_streetcode">
                            <xsl:value-of select="attr[@name='streetcode'])"/>
                        </xsl:variable>                 
                        <xsl:variable name="v_streetintake" select="attrQualMany[@name = 'streetintake']/value" />
                        <xsl:variable name="v_streetsizeDescription" select="attrQualMany[@name = 'streetsizeDescription']/value" />
                        <xsl:variable name="v_streetDetails" select="attrGroupMany[@name = 'streetDetails']/row" />                         
                        <xsl:for-each select="$v_streetintake">
                            <xsl:variable name="v_streetintakevalue" select="." />
                            <xsl:variable name="v_streetintakequal" select="./@qual" />
                            <xsl:for-each select="$v_streetsizeDescription">
                                <xsl:variable name="v_streetsizeDescriptionvalue" select="." />
                                <xsl:variable name="v_streetsizeDescriptionqual" select="./@qual" />
                                <xsl:for-each select="$v_streetDetails">
                                    <xsl:variable name="v_streetTypeCode">
                                        <xsl:value-of select="attr[@name='streetTypeCode'])"/>
                                    </xsl:variable> 
                                    <xsl:variable name="v_streetContained" select="attrQualMany[@name = 'streetContained']/value" />
                                    <xsl:for-each select="$v_streetContained">
                                        <xsl:variable name="v_streetContainedvalue" select="." />
                                        <xsl:variable name="v_streetContainedqual" select="./@qual" />
                                        <RelatedObjectId>
                                            <xsl:attribute name="referenceKey">                                                                      
                                                <xsl:value-of select="concat('ObjectId_Street','-',$v_position_streetinfo,'-',$v_streetcode,'-',$v_streetintakevalue,'-',$v_streetintakequal,'-',$v_streetsizeDescriptionvalue,'-',$v_streetsizeDescriptionqual,'-',$v_streetTypeCode,'-',$v_streetContainedvalue,'-',$v_streetContainedqual)"/>                         
                                            </xsl:attribute>                
                                        </RelatedObjectId> 
                                    </xsl:for-each>
                                </xsl:for-each>
                            </xsl:for-each>
                        </xsl:for-each>
                    </xsl:for-each>
                </RelatedObjectIds>
            </Relationship> 
        </RelationshipData>
    </CatalogObjectId>
</xsl:template>
</xsl:stylesheet>

Но это не работает, когда ни один из необязательных циклов не приходит. Я написал XSLT, когда присутствуют все необязательные циклы. Как я могу написать XSLT, когда в сообщении появляется какая-либо 1, 2, или 3, или ни одна из необязательных групп. Пожалуйста, предложите

Ожидаемый результат

Когда первая необязательная группа отсутствует, на выходе будет 8 записей.

<?xml version="1.0" encoding="UTF-8"?>
<CatalogObjectId>
  <RelationshipData>
   <Relationship>
     <RelationType>ObjectId_Street</RelationType>
     <RelatedObjectIds>
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz1-en-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz1-en-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz1-en-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz1-en-FAMS-5-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz2-hi-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz2-hi-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz2-hi-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en-sz2-hi-FAMS-5-OZA" />
     </RelatedObjectIds>
  </Relationship>
</RelationshipData>
</CatalogObjectId>

Когда первая и вторая необязательные группы отсутствуют, на выходе будет 4 записи.

<?xml version="1.0" encoding="UTF-8"?>
<CatalogObjectId>
<RelationshipData>
  <Relationship>
     <RelationType>ObjectId_Street</RelationType>
     <RelatedObjectIds>
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en--en-FAT-2-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en--en-FAT-3-OZA" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en--en-FAMS-4-ONZ" />
        <RelatedObjectId referenceKey="ObjectId_Street-1-AS_DRAINED--en--en-FAMS-5-OZA" />
     </RelatedObjectIds>
  </Relationship>
</RelationshipData>
</CatalogObjectId>

person Victor    schedule 04.01.2016    source источник
comment
Каков ваш ожидаемый результат, когда необязательная ‹строка› отсутствует?   -  person Rolf Rander    schedule 04.01.2016


Ответы (2)


Ok. Я не уверен, что полностью понимаю ваш ожидаемый результат. В вашем примере перечислены 4 необязательных цикла, а не два, и я не уверен, откуда берется «en» в вашем образце вывода с отсутствующими группами.

Тем не менее, это может помочь вам начать. Вместо того, чтобы пытаться свернуть все во вложенную структуру for-each, я использую несколько шаблонов в структуре, похожей на анализатор рекурсивного спуска. На каждом уровне код проверяет необязательные элементы и либо зацикливается на них, либо переходит на следующий уровень. Строки referenceKey передаются в качестве параметра и строятся по мере продвижения вниз.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/document">
        <CatalogObjectId>
            <RelationshipData>
                <Relationship>
                    <RelationType>ObjectId_Street</RelationType>
                    <RelatedObjectIds>
                        <xsl:for-each select="ObjectId/dlex/attrGroupMany[@name='streetinfo']">
                            <xsl:variable name="pos" select="position()"/>
                            <xsl:apply-templates select="row" mode="streetintake">
                                <xsl:with-param name="referenceKey" select="concat('ObjectId_Street-',$pos)" />
                            </xsl:apply-templates>
                        </xsl:for-each>
                    </RelatedObjectIds>
                </Relationship>
            </RelationshipData>
        </CatalogObjectId>
    </xsl:template>

    <xsl:template match="row" mode="streetintake">
        <xsl:param name="referenceKey" />
        <xsl:variable name="streetcode" select="concat($referenceKey,'-',attr[@name='streetcode'],'-')"/>
        <xsl:choose>
            <xsl:when test="attrQualMany[@name='streetintake']/value">
                <xsl:for-each select="attrQualMany[@name='streetintake']/value">
                    <xsl:apply-templates select="../.." mode="streetsize">
                        <xsl:with-param name="referenceKey" select="concat($streetcode,text(),'-',@qual)" />
                    </xsl:apply-templates>
                </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="." mode="streetsize">
                    <xsl:with-param name="referenceKey" select="$streetcode" />
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="row" mode="streetsize">
        <xsl:param name="referenceKey" />
        <xsl:choose>
            <xsl:when test="attrQualMany[@name='streetsizeDescription']/value">
                <xsl:for-each select="attrQualMany[@name='streetsizeDescription']/value">
                    <xsl:apply-templates select="../../attrGroupMany[@name='streetDetails']" mode="streetdetails">
                        <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',text(),'-',@qual)" />
                    </xsl:apply-templates>
                </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <xsl:apply-templates select="attrGroupMany[@name='streetDetails']" mode="streetdetails">
                    <xsl:with-param name="referenceKey" select="$referenceKey" />
                </xsl:apply-templates>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="attrGroupMany" mode="streetdetails">
        <xsl:param name="referenceKey" />
        <xsl:choose>
            <xsl:when test="row">
                <xsl:apply-templates select="row" mode="streetdetails">
                    <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',row/attr[@name='streetTypeCode'])" />
                </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="RelatedObjectId">
                    <xsl:with-param name="referenceKey" select="$referenceKey" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>        
    </xsl:template>

    <xsl:template match="row" mode="streetdetails">
        <xsl:param name="referenceKey" />
        <xsl:choose>
            <xsl:when test="attrQualMany[@name='streetContained']">
                <xsl:apply-templates select="attrQualMany[@name='streetContained']">
                    <xsl:with-param name="referenceKey" select="$referenceKey"/>
                </xsl:apply-templates>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="RelatedObjectId">
                    <xsl:with-param name="referenceKey" select="$referenceKey" />
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>        
    </xsl:template>

    <xsl:template match="attrQualMany[@name='streetContained']">
        <xsl:param name="referenceKey" />
        <xsl:choose>
            <xsl:when test="value">
                <xsl:for-each select="value">
                    <xsl:call-template name="RelatedObjectId">
                        <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',text(),'-',@qual)" />
                    </xsl:call-template>
                </xsl:for-each>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="RelatedObjectId">
                    <xsl:with-param name="referenceKey" select="$referenceKey" />
                </xsl:call-template>                
            </xsl:otherwise>
        </xsl:choose>        
    </xsl:template>

    <xsl:template name="RelatedObjectId">
        <xsl:param name="referenceKey" />
        <RelatedObjectId>
            <xsl:attribute name="referenceKey"><xsl:value-of select="$referenceKey"/></xsl:attribute>
        </RelatedObjectId>        
    </xsl:template>

</xsl:stylesheet>
person Rolf Rander    schedule 04.01.2016

Я добавил еще несколько фрагментов в существующий код для обработки нулевого сценария, когда все необязательные группы отсутствуют или когда полный

  <attrGroupMany name="streetDetails"> details are missing.

<xsl:output method="xml" indent="yes"/>

<xsl:template match="/document">
    <CatalogObjectId>
        <RelationshipData>
            <Relationship>
                <RelationType>ObjectId_Street</RelationType>
                <RelatedObjectIds>
                    <xsl:for-each select="ObjectId/dlex/attrGroupMany[@name='streetinfo']">
                        <xsl:variable name="pos" select="position()"/>
                        <xsl:apply-templates select="row" mode="streetintake">
                            <xsl:with-param name="referenceKey" select="concat('ObjectId_Street-',$pos)" />
                        </xsl:apply-templates>
                    </xsl:for-each>
                </RelatedObjectIds>
            </Relationship>
        </RelationshipData>
    </CatalogObjectId>
</xsl:template>

<xsl:template match="row" mode="streetintake">
    <xsl:param name="referenceKey" />
    <xsl:variable name="streetcode" select="concat($referenceKey,'-',attr[@name='streetcode'],'-')"/>
    <xsl:choose>
        <xsl:when test="attrQualMany[@name='streetintake']/value">
            <xsl:for-each select="attrQualMany[@name='streetintake']/value">
                <xsl:apply-templates select="../.." mode="streetsize">
                    <xsl:with-param name="referenceKey" select="concat($streetcode,text(),'-',@qual)" />
                </xsl:apply-templates>
            </xsl:for-each>
        </xsl:when>
        <xsl:otherwise>
            <xsl:apply-templates select="." mode="streetsize">
                <xsl:with-param name="referenceKey" select="$streetcode" />
            </xsl:apply-templates>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="row" mode="streetsize">
    <xsl:param name="referenceKey" />
    <xsl:choose>
        <xsl:when test="attrQualMany[@name='streetsizeDescription']/value">
            <xsl:choose>
                <xsl:when test="attrGroupMany[@name='streetDetails']">
                    <xsl:for-each select="attrQualMany[@name='streetsizeDescription']/value">
                        <xsl:apply-templates select="../../attrGroupMany[@name='streetDetails']" mode="streetdetails">
                            <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',text(),'-',@qual)" />
                        </xsl:apply-templates> 
                    </xsl:for-each>
                </xsl:when>
                <xsl:otherwise>
                    <xsl:for-each select="attrQualMany[@name='streetsizeDescription']/value">
                        <xsl:call-template name="RelatedObjectId">
                            <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',text(),'-',@qual)" />
                        </xsl:call-template>   
                    </xsl:for-each>
                </xsl:otherwise>
            </xsl:choose>
        </xsl:when>
        <xsl:otherwise>
            <xsl:if test="not(attrQualMany[@name='streetintake']) and not(attrQualMany[@name='streetsizeDescription']) and not(attrGroupMany[@name='streetDetails'])">
                <xsl:call-template name="RelatedObjectId">
                    <xsl:with-param name="referenceKey" select="$referenceKey" />
                </xsl:call-template>                
            </xsl:if>
            <xsl:apply-templates select="attrGroupMany[@name='streetDetails']" mode="streetdetails">
                <xsl:with-param name="referenceKey" select="$referenceKey" />
            </xsl:apply-templates>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="attrGroupMany" mode="streetdetails">
    <xsl:param name="referenceKey" />
    <xsl:choose>
        <xsl:when test="row">
            <xsl:apply-templates select="row" mode="streetdetails">
                <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',row/attr[@name='streetTypeCode'])" />
            </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="RelatedObjectId">
                <xsl:with-param name="referenceKey" select="$referenceKey" />
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>        
</xsl:template>

<xsl:template match="row" mode="streetdetails">
    <xsl:param name="referenceKey" />
    <xsl:choose>
        <xsl:when test="attrQualMany[@name='streetContained']">
            <xsl:apply-templates select="attrQualMany[@name='streetContained']">
                <xsl:with-param name="referenceKey" select="$referenceKey"/>
            </xsl:apply-templates>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="RelatedObjectId">
                <xsl:with-param name="referenceKey" select="$referenceKey" />
            </xsl:call-template>
        </xsl:otherwise>
    </xsl:choose>        
</xsl:template>

<xsl:template match="attrQualMany[@name='streetContained']">
    <xsl:param name="referenceKey" />
    <xsl:choose>
        <xsl:when test="value">
            <xsl:for-each select="value">
                <xsl:call-template name="RelatedObjectId">
                    <xsl:with-param name="referenceKey" select="concat($referenceKey,'-',text(),'-',@qual)" />
                </xsl:call-template>
            </xsl:for-each>
        </xsl:when>
        <xsl:otherwise>
            <xsl:call-template name="RelatedObjectId">
                <xsl:with-param name="referenceKey" select="$referenceKey" />
            </xsl:call-template>                
        </xsl:otherwise>
    </xsl:choose>        
</xsl:template>

<xsl:template name="RelatedObjectId">
    <xsl:param name="referenceKey" />
    <RelatedObjectId>
        <xsl:attribute name="referenceKey">
            <xsl:value-of select="$referenceKey"/>
        </xsl:attribute>
    </RelatedObjectId>        
</xsl:template>

person Victor    schedule 05.01.2016