在 XSLT 中处理多个条件循环

Handling multiple Conditional Looping in 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,当消息中出现可选组的任何 1 或 2 或 3 或 none 时,我如何编写 XSLT。请建议

预期输出

当第一个可选组不存在时,输出将有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>

好的。我不确定我是否完全理解您的预期输出。您的示例列出了 4 个可选循环,而不是两个,而且我不确定示例输出中缺少组的 "en" 来自哪里。

但是,这可以帮助您入门。我没有尝试在嵌套 for-each 结构中汇总所有内容,而是在类似于递归下降解析器的结构中使用多个模板。在每个级别,代码都会检查可选元素并在它们周围循环或跳到下一个级别。 referenceKey-strings 作为参数传递并在我们向下构建时构建。

<?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>

我在现有代码中添加了更多片段,以处理所有可选组都缺失或完整

时的空情况
  <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>