xml XSLT:如何从我的结果中排除空元素?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2706202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
XSLT: How to exclude empty elements from my result?
提问by Fedor Steeman
I have a rather complicated xslt sheet transforming one xml format to another using templates. However, in the resulting xml, I need to have all the empty elements excluded. How is that done?
我有一个相当复杂的 xslt 表,它使用模板将一种 xml 格式转换为另一种格式。但是,在生成的 xml 中,我需要排除所有空元素。这是怎么做的?
This is how the base xslt looks like:
这是基本 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>
What needs to be added, so that empty elements are left out?
需要添加什么,以便排除空元素?
For example, a snippet from the resulting xml:
例如,来自生成的 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>
That really needs to be:
那真的需要:
<?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>
In other words: Empty elements should be left out.
换句话说:空元素应该被排除在外。
回答by Dimitre Novatchev
The provided (partial) XSLT code illustrates well an XSLT antipattern. Try almost always to avoid the use of <xsl:for-each>.
提供的(部分)XSLT 代码很好地说明了 XSLT 反模式。几乎总是尽量避免使用<xsl:for-each>.
Below there is a sample XML document and a transformation which copies all nodes with the exception of the "empty" elements. Here by "empty" we mean either childless, or with one child whitespace-only child node.
下面是一个示例 XML 文档和一个转换,它复制除“空”元素之外的所有节点。这里的“空”是指没有子节点,或者有一个子节点,只有空白子节点。
XML Document:
XML 文件:
<a>
<b>
<c> </c>
<d/>
<e>1</e>
</b>
</a>
Transformation:
转型:
<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>
Result:
结果:
<a>
<b>
<e>1</e>
</b>
</a>
Do note:
请注意:
The use of the Identity Rule.
How we override the Identity Rulewith a template that only matches "empty" elements. As this template does nothing (has no body at all), this doesn't copy ("deletes") the "empty" elements.
身份规则的使用。
我们如何使用仅匹配“空”元素的模板覆盖身份规则。由于此模板什么都不做(根本没有主体),因此不会复制(“删除”)“空”元素。
Using and overriding the Identity Rule is the most important XSLT design pattern.
使用和覆盖 Identity Rule 是最重要的 XSLT 设计模式。
回答by ChaosPandion
This is probably the simplest way:
这可能是最简单的方法:
<xsl:for-each select="Nodes/Node[text() != '']">
</xsl:for-each>
If you have control of the XML generation then don't add the root node if there is no children. Regardless of which way you choose XSL is quite verbose.
如果您可以控制 XML 生成,则在没有子节点的情况下不要添加根节点。无论您选择哪种方式,XSL 都是相当冗长的。
回答by Robert Rossney
There are some tricky cases where Dimitre's answer (which is certainly the right approach) might behave unexpectedly. For instance, if you've refactored your XSLT to use the identity pattern (which you should), and you have created a template like this:
在某些棘手的情况下,Dimitre 的回答(这当然是正确的方法)可能会出乎意料。例如,如果您重构了您的 XSLT 以使用身份模式(您应该这样做),并且您已经创建了一个这样的模板:
<xsl:template match="Vehicle/TransportShiftNumber[. != '123']">
<EdiActivevehicle>
<xsl:value-of select="."/>
</EdiActivevehicle>
</xsl:template>
the transform may still create empty EdiActivevehicleelements if TransportShiftNumberis empty.
EdiActivevehicle如果TransportShiftNumber为空,转换可能仍会创建空元素。
Ordinarily, if multiple templates match a node, the one that's more specific will be selected. "More specific" typically means that patterns that have a predicate will beat out patterns that don't. (The actual conflict-resolution rules are more involved; see section 5.5 of the XSLT recommendation.) In this case, both the above template and the empty-element template use predicates, and thus both have the same priority.
通常,如果多个模板与一个节点匹配,则将选择更具体的模板。“更具体”通常意味着具有谓词的模式将击败没有谓词的模式。(实际的冲突解决规则更多;请参阅 XSLT 建议的第 5.5 节。)在这种情况下,上述模板和空元素模板都使用谓词,因此两者具有相同的优先级。
So the XSLT processor will do one of two things: it will report an error (that's allowed, though I've never seen an XSLT processor that unfriendly), or it will select the template that appears latestin the stylesheet.
因此,XSLT 处理器将执行以下两种操作之一:它会报告错误(这是允许的,尽管我从未见过不友好的 XSLT 处理器),或者它会选择样式表中最新出现的模板。
There are two ways to fix this. Either put the empty-element-filtering template at the bottom of the stylesheet, or explicitly assign it a priority that's higher then 0.5 (which is the default value for most patterns that have predicates):
有两种方法可以解决这个问题。要么将空元素过滤模板放在样式表的底部,要么明确地为其分配一个高于 0.5 的优先级(这是大多数具有谓词的模式的默认值):
I'd probably do the latter, because I generally structure stylesheets with the expectation that the ordering of templates is not significant and I don't want any nasty surprises if I start moving things around. But I'd sure put a comment in there explaining myself: I've never seen anyone actually use an explicit priority on a template.
我可能会选择后者,因为我通常在构建样式表时期望模板的顺序并不重要,并且如果我开始移动事物,我不希望任何令人讨厌的惊喜。但我肯定会在那里发表评论来解释我自己:我从未见过有人真正在模板上使用明确的优先级。
回答by Kurt Smith
I started with Dimitre's solution above (thanks!) but I still had output or null elements with null children like so:
我从上面的 Dimitre 解决方案开始(谢谢!)但我仍然有输出或 null 元素的 null 子元素,如下所示:
<a>
<b>
<c/>
<d/>
</b>
</a>
This seems to work... still testing.
这似乎有效......仍在测试中。
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:date="http://exslt.org/dates-and-times"
xmlns:exsl="http://exslt.org/common"
xmlns:func="http://exslt.org/common"
xmlns:random="http://exslt.org/random"
xmlns:regexp="http://exslt.org/regular-expressions"
xmlns:set="http://exslt.org/sets"
xmlns:str="http://exslt.org/strings"
version="1.0"
extension-element-prefixes="date exsl func random regexp set str">
<xsl:output
method="xml"
encoding="utf-8"
omit-xml-declaration="no"
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(string())]
"/>
</xsl:stylesheet>

