【问题标题】:Removing empty XML elements after creating new elements创建新元素后删除空 XML 元素
【发布时间】:2020-03-27 19:09:37
【问题描述】:

我知道网上有很多关于如何使用 XSLT 删除空 XML 元素的示例,但是我找不到适用于我的文档的方法。我创建了编写新元素的模板,并希望处理一个最终模板,该模板匹配所有内部没有文本内容的元素,这包括子元素。如果一个元素包含一个没有文本的子元素,我希望只删除空的子元素。我尝试过使用识别转换,但它们似乎不适用于我设置模板的方式,但我想保留这种结构。

XML

<fruitSet>
    <fruit>
        <name>apple</name>
        <colour>green</colour>
        <ratings>
            <taste>10</taste>
            <look>8</look>
        </ratings>
    </fruit>
    <fruit>
        <name>strawberry</name>
        <colour>red</colour>
        <ratings>
            <taste>7</taste>
            <look>5</look>
        </ratings>
    </fruit>    
        <fruit>
        <name>Orange</name>
        <colour></colour>
        <ratings>
            <taste>3</taste>
            <look></look>
        </ratings>
    </fruit>
</fruitSet>

XSL

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xsd ="http://www.w3.org/2001/XMLSchema#">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>

<xsl:template match="/">                
    <RDF>
        <xsl:apply-templates select = "fruitSet/fruit"/>
        <xsl:apply-templates select = "fruitSet/fruit/ratings"/>

    </RDF>
</xsl:template> 

<xsl:template match = "fruit">
    <Description>
        <xsl:attribute name = "ID">
            <xsl:value-of select = "name/text()"/>
        </xsl:attribute>
        <hasColour><xsl:value-of select = "colour/text()"/></hasColour>
        <hasTasteRating>
            <xsl:apply-templates select = "ratings"/>
        </hasTasteRating>
    </Description>
</xsl:template>


<xsl:template match = "ratings">
    <xsl:value-of select = "taste/text()"/>
</xsl:template>




</xsl:stylesheet>

期望的输出

<?xml version="1.0" encoding="utf-16"?>
<RDF xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
  <Description ID="apple">
    <hasColour>green</hasColour>
    <hasTasteRating>10</hasTasteRating>
  </Description>
  <Description ID="strawberry">
    <hasColour>red</hasColour>
    <hasTasteRating>7</hasTasteRating>
  </Description>
  <Description ID="Orange">
    <hasTasteRating>3</hasTasteRating>
  </Description>1073</RDF>
</RDF>

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    您可以使用以下 XSLT-1.0 样式表。它实现了两种解决问题的方法:

    1. 应用xsl:if 来跳过输出中不需要的元素。
    2. 使用谓词限制通用元素模板

    这是整个样式表:

    <xsl:stylesheet version="1.0" 
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xsd ="http://www.w3.org/2001/XMLSchema#">
        <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
    
        <xsl:template match="/">                
            <RDF>
                <xsl:apply-templates select = "fruitSet/fruit"/>
            </RDF>
        </xsl:template>
    
        <xsl:template match = "fruit">
            <Description>
                <xsl:attribute name = "ID">
                    <xsl:value-of select = "name/text()"/>
                </xsl:attribute>
                <xsl:if test="normalize-space(colour)">
                    <hasColour>
                        <xsl:value-of select = "colour"/>
                    </hasColour>
                </xsl:if>
                <xsl:apply-templates select="ratings" />
            </Description>
        </xsl:template>
    
        <!-- Only create a <hasTasteRating> element for all elements with a present <taste>  child -->
        <xsl:template match="*[normalize-space(taste)]" >
            <hasTasteRating>
                <xsl:value-of select="taste/text()"/>
            </hasTasteRating>
        </xsl:template>
    
    </xsl:stylesheet>
    

    它的输出是:

    <?xml version="1.0" encoding="UTF-8"?>
    <RDF xmlns:xsd="http://www.w3.org/2001/XMLSchema#">
        <Description ID="apple">
            <hasColour>green</hasColour>
            <hasTasteRating>10</hasTasteRating>
        </Description>
        <Description ID="strawberry">
            <hasColour>red</hasColour>
            <hasTasteRating>7</hasTasteRating>
        </Description>
        <Description ID="Orange">
            <hasTasteRating>3</hasTasteRating>
        </Description>
    </RDF>
    

    【讨论】:

    • 这很好用,但是您知道是否有一种更通用的方法可以使用一个模板删除整个输出?例如,如果有更多的子元素或另一个集合,例如带有肉元素的肉集,等等。
    • 我改进了我的解决方案。现在更通用了。但是,如果结果元素名称与匹配的元素名称不同,则很难创建真正通用的解决方案。这只能通过通用模式或元素名称和结果元素名称之间的键映射来完成..
    • 是的,现在好多了,虽然现在我想起来了,创建另一个样式表来处理输出可能更容易