XSLT:获取具有特定子元素且没有文本的元素

时间:2019-03-15 16:52:29

标签: xml xslt

嗨,我有一个xml文件,如下所示:

<root>
    <body>
        <place attr="1" id="1" ref="www.example.com">
            <name>abc
            </name>
        </place>
        <place attr="1" id="2" ref="www.example.com">
            <name>def
            </name>
        </place>
        <place attr="2" id="3">
            <place attr="3" id="4" ref="www.example.com">
                <name>efg
                </name>
            </place>
        </place>
    </body>
</root>

我想获取所有具有子元素<place>且元素之间没有文本的元素<name>

我想要的输出是这样的:

<root>
   <place attr="1" id="1" ref="www.example.com" />
   <place attr="1" id="2" ref="www.example.com" />
   <place attr="3" id="4" ref="www.example.com" />
</root>

我的xsl代码返回所有<place>标签(我不想要)+ <name>标签之间的文本:

   <xsl:template match="root">       
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/> 
        </xsl:copy>
    </xsl:template>

   <xsl:template match="place">       
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <xsl:apply-templates/> 
        </xsl:copy>
    </xsl:template>

xml输出:

<?xml version="1.0" encoding="UTF-8"?>
<root>
   <place attr="1" id="1" ref="www.example.com">abc</place>
   <place attr="1" id="2" ref="www.example.com">def</place>
   <place attr="2" id="3">
      <place attr="3" id="4" ref="www.example.com">efg</place>
   </place>
</root>

3 个答案:

答案 0 :(得分:1)

关键是您的apply-templates语句。

如您所述,您“只想要<place>标签,并且在孩童时期拥有<name>。因此,您应该只选择那些。

您当前的代码:

<xsl:template match="root">
    <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
    </xsl:copy>
</xsl:template>

仅使用<xsl:apply-templates/>时,这与说“将模板应用于此下的 *一切* ”相同。因此,您甚至可以捕获甚至不需要的那些<place>元素。

为了更具选择性并得到您想要的,我们可以说:

<xsl:apply-templates select="descendant::place[name]"/>

打破现状

我们使用select语句进行选择。 :)我们不想将模板应用于所有,而仅应用于特定事物,这就是我们指定哪些事物的方式。

我们使用descendant::轴是因为我们想捕获<place>元素中包含的所有<root>元素。如果我们只说select="place[name]",则意味着“仅将模板应用于这些<place>元素的直接子元素<root>。因为没有<place>元素是此<root>元素的直接子元素,对您没有任何帮助。

我们使用[name]谓词来指定条件,即我们只希望具有<place>个子元素的<name>个元素。这样一来,我们就排除了<place attr="2" id="3">,后者包含另一个<place>,但没有自己的任何<name>子代。

我希望这会有所帮助。如果您有任何您不了解的地方,请发表评论。

更新:完整的工作示例

我错误地估计了较早答案中要包含的详细信息。 :)

这是一个带有注释的完整示例。

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="1.0">

    <!-- Start at the logical root / since every XSLT *must* start there. -->
    <xsl:template match="/">
        <!-- We just pass everything along.  NOTE: We NEED to define templates
            for anything special we want to do, beyond just the default 
            XSLT behavior of outputting the string contents minus any elements. -->
        <xsl:apply-templates/>
    </xsl:template>

    <!-- We want to get the <root> element and attributes in our output, so
        we define a template to do that.-->
    <xsl:template match="root">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
            <!-- We also want to process the content of <root>, so we use 
                `xsl:apply-templates`.  Since we also want to be _selective_
                about what we process, we also specify a `select` statement
                with the XPath needed to identify what we want.  
                Again, we NEEd to define a template to process this,
                or we'll just get the text string content and none of 
                the elements.  -->
            <xsl:apply-templates select="descendant::place[name]"/>
        </xsl:copy>
    </xsl:template>

    <!-- Here we define what to do with the <place> elements that have
        <name> children.  Since your <place attr="2" id="3"> element
        has no <name> child, it gets omitted from the output. -->
    <xsl:template match="place[name]">
        <xsl:copy>
            <xsl:copy-of select="@*"/>
        </xsl:copy>
    </xsl:template>

    <!-- Lastly, we define one more template that says "capture everything 
        else, and *don't* output anything".  This way, we don't get the text 
        string output of anything we haven't explicitly defined above. -->
    <xsl:template match="*"/>

</xsl:stylesheet>

我们只得到所需的元素,而没有包含它们的文本。

答案 1 :(得分:1)

要获得请求的输出,您可以简单地执行以下操作:

XSLT 1.0

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

<xsl:template match="/root">
    <xsl:copy>
        <xsl:for-each select=".//place[name]">
            <xsl:copy>
                <xsl:copy-of select="@*"/>
            </xsl:copy>
        </xsl:for-each>
    </xsl:copy>
</xsl:template>

</xsl:stylesheet>

答案 2 :(得分:1)

首先,您需要了解XSLT process model:从输入文档的文档根目录开始,处理器尝试将此节点与某些模板的模式所描述的规则进行匹配。 built-in rules有一些:基本上,它们逐级遍历输入文档,并按文档顺序从头到尾遍历。

这就是为什么您需要一条规则来复制rootplace元素,以及一条不输出文本节点的规则。

使用此输入

<root>
    <body>
        <place attr="1" id="1" ref="www.example.com">
            <name>abc
            </name>
        </place>
        <place attr="1" id="2" ref="www.example.com">
            <name>def
            </name>
        </place>
        <place attr="2" id="3">
            <place attr="3" id="4" ref="www.example.com">
                <name>efg
                </name>
            </place>
        </place>
    </body>
</root>

此样式表

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="root|place[name]">
        <xsl:copy>
        <xsl:copy-of select="@*"/>
        <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="text()"/>
</xsl:stylesheet>

输出

<root>
    <place attr="1" id="1" ref="www.example.com" />
    <place attr="1" id="2" ref="www.example.com" />
    <place attr="3" id="4" ref="www.example.com" />
</root>