【问题标题】:XSLT: Get an element that has a specific sub element and without textXSLT:获取具有特定子元素且没有文本的元素
【发布时间】:2019-03-16 04:05:30
【问题描述】:

您好,我有一个 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>

我想获取所有元素 &lt;place&gt; 有一个子 &lt;name&gt; 并且元素之间没有文本。

我想要的输出是这样的:

<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 代码返回所有 &lt;place&gt; 标签(我不想要)+ &lt;name&gt; 标签之间的文本:

   <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>

【问题讨论】:

  • 那么你到底在哪里坚持这个?
  • 请发布您的 XSLT 代码。正如迈克尔指出的那样,如果我们不知道您的具体问题是什么,我们将无法有效地帮助您。 “我要XXX”不是问题,是请求。
  • 我这样做了:xml &lt;xsl:template match="place"&gt; &lt;xsl:copy&gt; &lt;xsl:copy-of select="@*"/&gt; &lt;xsl:apply-templates/&gt; &lt;/xsl:copy&gt; &lt;/xsl:template&gt; 我得到了所有带有文本的 标记,但无法得到我的示例中提到的 标记后面的那些
  • 请使用您的工作 XSL 代码更新您的帖子。理想情况下,如果您提供minimal, complete, and verifiable example,我们将能够为您提供最好的帮助。
  • 是的,我会的,忘记添加谢谢。

标签: xml xslt


【解决方案1】:

关键是你的apply-templates 声明。

正如您所说,您“只想要 &lt;place&gt; 标记作为孩子的 &lt;name&gt;”。所以你应该只选择那些。

您当前的代码:

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

当您只使用&lt;xsl:apply-templates/&gt; 时,这与说“将模板应用于此下的*everything*”相同。所以你最终会捕捉到那些你不想要的&lt;place&gt;元素。

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

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

打破一切

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

我们使用descendant:: 轴是因为我们想要捕获包含在&lt;root&gt; 元素(即其后代)中的所有&lt;place&gt; 元素。如果我们只说select="place[name]",那将意味着“仅将模板应用于作为此&lt;root&gt; 元素的直接子元素的那些&lt;place&gt; 元素。因为没有任何&lt;place&gt; 元素是此@ 的直接子元素987654336@ 元素,这对你没有任何用处。

我们使用[name] 谓词来指定我们只想要具有&lt;name&gt; 子元素的&lt;place&gt; 元素的条件。这让我们可以排除 &lt;place attr="2" id="3"&gt;,它包含另一个 &lt;place&gt;,但没有任何自己的 &lt;name&gt; 子代。

我希望这会有所帮助。如果这里有什么不明白的地方请评论。

更新:完整的工作示例

我错误地估计了我之前的答案中包含多少细节。 :)

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

<?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>

我们只得到想要的元素,没有它们包含的文本。

【讨论】:

  • 感谢您的详细回答,我按照您所说的做了,但不幸的是,我仍然收到 标签之间的文本(我也不想要它)并且仍然收到 &lt;place attr="2" id="3"&gt;
  • @mchahoud,“ 标签之间的文本”是什么意思?你是说空格吗?
  • @mchahoud,另外,你把你的select 声明放在哪里了?我有你的示例代码和我的修改,我根本没有得到&lt;place attr="2" id="3"&gt;。
  • ``` ``` 这段代码我只得到abc def efg
  • 谢谢,现在我清楚地了解了您选择每个元素并应用模板以省略我们不想要的内容的过程!干得好:)
【解决方案2】:

要获得请求的输出,您可以这样做:

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>

【讨论】:

  • 感谢此代码适用于此示例,但如果没有 for-each,我们如何继续
  • 为什么要“在没有for-each 的情况下继续”?
  • @michael.hor257k 我想你明白为什么我们总是以隐式或显式风格呈现 XSLT 解决方案。
  • @Alejandro 恐怕我不知道你在说什么。
  • @zx485 一点也不。这意味着这两种方法都服务于不同的上下文。显式结构便于维护,因为它们易于可视化。隐式解决方案适用于扩展其架构的源。
【解决方案3】:

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

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

有了这个输入

<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>

【讨论】:

    猜你喜欢
    • 2011-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-09
    • 2016-02-29
    相关资源
    最近更新 更多