XSLT:如何从结果中排除空元素?

时间:2010-04-24 21:21:40

标签: xml xslt

我有一个相当复杂的xslt表,使用模板将一个xml格式转换为另一个xml格式。但是,在生成的xml中,我需要排除所有空元素。怎么做的?

这就是基础xslt的样子:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:far="http://www.itella.com/fargo/fargogate/" xmlns:a="http://tempuri.org/XMLSchema.xsd" xmlns:p="http://tempuri.org/XMLSchema.xsd">
    <xsl:import href="TransportCDMtoFDM_V0.6.xsl"/>
    <xsl:import href="ConsignmentCDMtoFDM_V0.6.xsl"/>
    <xsl:template match="/">
        <InboundFargoMessage>
            <EdiSender>
                <xsl:value-of select="TransportInformationMessage/SenderId"/>
            </EdiSender>
            <EdiReceiver>
                <xsl:value-of select="TransportInformationMessage/RecipientId"/>
            </EdiReceiver>
            <EdiSource>
                <xsl:value-of select="TransportInformationMessage/Waybill/Parties/Consignor/Id"/>
            </EdiSource>
            <EdiDestination>FARGO</EdiDestination>
            <Transportations>
                <xsl:for-each select="TransportInformationMessage/TransportUnits/TransportUnit">
                    <xsl:call-template name="transport"/>
                </xsl:for-each>
                <xsl:for-each select="TransportInformationMessage/Waybill/TransportUnits/TransportUnit">
                    <xsl:call-template name="transport"/>
                </xsl:for-each>
                <xsl:for-each select="TransportInformationMessage/Waybill">
                    <EdiImportTransportationDTO>
                        <Consignments>
                            <xsl:for-each select="Shipments/Shipment">
                                <xsl:call-template name="consignment"/>
                            </xsl:for-each>
                        </Consignments>
                        <EdiTerminalDepartureTime>
                            <xsl:value-of select="DatesAndTimes/EstimatedDepartureDateTime"/>
                            <xsl:value-of select="DatesAndTimes/DepartureDateTime"/>
                        </EdiTerminalDepartureTime>
                        <EdiAgentTerminalArrivalDate>
                            <xsl:value-of select="DatesAndTimes/EstimatedArrivalDateTime"/>
                            <xsl:value-of select="DatesAndTimes/ArrivalDateTime"/>
                        </EdiAgentTerminalArrivalDate>
                        <EdiActivevehicle>
                            <xsl:value-of select="Vehicle/TransportShiftNumber"/>
                        </EdiActivevehicle>
                        <EdiConveyerZipCodeTown><xsl:text> </xsl:text></EdiConveyerZipCodeTown>
                    </EdiImportTransportationDTO>
                </xsl:for-each>
            </Transportations>
        </InboundFargoMessage>
    </xsl:template>
</xsl:stylesheet>

需要添加什么,以便省略空元素?

例如,生成的xml:

中的一个片段
<?xml version="1.0" encoding="UTF-8"?>
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd"
        xmlns:far="http://www.itella.com/fargo/fargogate/"
        xmlns:a="http://tempuri.org/XMLSchema.xsd">
    <EdiSender>XXXX</EdiSender>
    <EdiReceiver>YYYY</EdiReceiver>
    <EdiSource>TR/BAL/IST</EdiSource>
    <EdiDestination>FARGO</EdiDestination>
    <Transportations>
        <EdiImportTransportationDTO>
            <Consignments>
                <EdiImportConsignmentDTO>
                    <ConsignmentLines>
                        <EdiImportConsignmentLineDTO>
                            <DangerousGoodsItems>
                                <EdiImportDangerGoodsItemDTO>
                                    <EdiKolliTypeOuter/>
                                    <EdiKolliTypeInner/>
                                    <EdiTechnicalDescription/>
                                    <EdiUNno/>
                                    <EdiClass/>
                                    <EdiDangerFactor/>
                                    <EdiEmergencyTemperature/>
                                </EdiImportDangerGoodsItemDTO>
                            </DangerousGoodsItems>
                            <BarCodes>
                                <EdiImportConsignmentLineBarcodeDTO/>
                            </BarCodes>
                            <EdiNumberOfPieces>00000002</EdiNumberOfPieces>
                            <EdiGrossWeight>0.000</EdiGrossWeight>
                            <EdiHeight/>
                            <EdiWidth/>
                            <EdiLength/>
                            <EdiGoodsDescription/>
                            <EdiMarkingAndNumber/>
                            <EdiKolliType>road</EdiKolliType>
                            <EdiCbm/>
                            <EdiLdm/>
                        </EdiImportConsignmentLineDTO>

真的需要:

<?xml version="1.0" encoding="UTF-8"?>
<InboundFargoMessage xmlns:p="http://tempuri.org/XMLSchema.xsd"
        xmlns:far="http://www.itella.com/fargo/fargogate/"
        xmlns:a="http://tempuri.org/XMLSchema.xsd">
    <EdiSender>XXXX</EdiSender>
    <EdiReceiver>YYYY</EdiReceiver>
    <EdiSource>TR/BAL/IST</EdiSource>
    <EdiDestination>FARGO</EdiDestination>
    <Transportations>
        <EdiImportTransportationDTO>
            <Consignments>
                <EdiImportConsignmentDTO>
                    <ConsignmentLines>
                        <EdiImportConsignmentLineDTO>
                            <DangerousGoodsItems/>
                            <BarCodes/>
                            <EdiNumberOfPieces>00000002</EdiNumberOfPieces>
                            <EdiGrossWeight>0.000</EdiGrossWeight>
                            <EdiKolliType>road</EdiKolliType>
                        </EdiImportConsignmentLineDTO>

换句话说:应该省略空元素。

3 个答案:

答案 0 :(得分:11)

提供的(部分)XSLT代码很好地说明了XSLT反模式。请务必尝试避免使用<xsl:for-each>

下面是一个示例XML文档和一个转换,它复制除“空”元素之外的所有节点。这里“空”是指无子女,或者只有一个子空白子节点。

XML文档

<a>
 <b>
   <c>  </c>
   <d/>
   <e>1</e>
 </b>
</a>

<强>转化

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

 <xsl:strip-space elements="*"/>

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

 <xsl:template match=
  "*[not(node())]
  |
   *[not(node()[2])
   and
     node()/self::text()
   and
     not(normalize-space())
     ]
  "/>
</xsl:stylesheet>

<强>结果:

<a>
   <b>
      <e>1</e>
   </b>
</a>

请注意

  1. 使用身份规则

  2. 我们如何使用仅匹配“空”元素的模板覆盖标识规则。由于此模板不执行任何操作(根本没有正文),因此不会复制(“删除”)“空”元素。

  3. 使用和覆盖标识规则是最重要的XSLT设计模式。

答案 1 :(得分:1)

这可能是最简单的方法:

<xsl:for-each select="Nodes/Node[text() != '']">

</xsl:for-each>

如果您已控制XML生成,则在没有子项时不要添加根节点。无论你选择哪种方式,XSL都非常冗长。

答案 2 :(得分:0)

有一些棘手的案例,Dimitre的答案(肯定是正确的方法)可能会出乎意料地表现出来。例如,如果您重构了XSLT以使用身份模式(您应该),并且您已经创建了这样的模板:

<xsl:template match="Vehicle/TransportShiftNumber[. != '123']">
   <EdiActivevehicle>
      <xsl:value-of select="."/>
   </EdiActivevehicle> 
</xsl:template>

如果EdiActivevehicle为空,转换可能仍会创建空的TransportShiftNumber元素。

通常,如果多个模板与节点匹配,则会选择更具体的模板。 “更具体”通常意味着具有谓词的模式将击败不具有谓词的模式。 (实际的冲突解决规则更复杂;请参阅XSLT建议的第5.5节。)在这种情况下,上面的模板和空元素模板都使用谓词,因此两者具有相同的优先级。

因此,XSLT处理器将执行以下两项操作之一:它将报告错误(虽然我从未见过不友好的XSLT处理器,但它会报告错误),否则它将选择 latest 在样式表中。

有两种方法可以解决这个问题。将空元素过滤模板放在样式表的底部,或者显式地为其分配一个高于0.5的优先级(这是大多数具有谓词的模式的默认值):

我可能会做后者,因为我通常构造样式表,期望模板的排序不重要,如果我开始移动东西,我不想要任何令人讨厌的惊喜。但我肯定会在那里发表评论来解释自己:我从未见过任何人真正在模板上使用明确的优先权。