我有一个相当复杂的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>
换句话说:应该省略空元素。
答案 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>
请注意:
使用身份规则。
我们如何使用仅匹配“空”元素的模板覆盖标识规则。由于此模板不执行任何操作(根本没有正文),因此不会复制(“删除”)“空”元素。
使用和覆盖标识规则是最重要的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的优先级(这是大多数具有谓词的模式的默认值):
我可能会做后者,因为我通常构造样式表,期望模板的排序不重要,如果我开始移动东西,我不想要任何令人讨厌的惊喜。但我肯定会在那里发表评论来解释自己:我从未见过任何人真正在模板上使用明确的优先权。