【问题标题】:XSL with multiple templates具有多个模板的 XSL
【发布时间】:2018-08-15 12:20:24
【问题描述】:

我正在尝试使用以下步骤创建一个转换 XML 的 XSL:

  1. 确定根“properties”下的元素是否具有以“foo”开头的名称。如果它不以 'foo' 开头,则删除该元素。
  2. 创建名为“entry”的新元素,其属性“key”等于以“foo”开头的元素名称。

如果看起来更容易,该过程也可以按照指定步骤的相反顺序进行。

XML 文档

<?xml version="1.0" encoding="UTF-8"?>
<properties>
    <oof>AAA</oof>
    <bar>BBB</bar>
    <foobar>CCC</foobar>
    <barfoo>DDD</barfoo>
    <foofoofoobar>EEE</foofoofoobar>
</properties>

我的 XSLT 只将元素更改为条目

<?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="2.0">

  <xsl:template match="node()|@*" name="identity">
     <xsl:copy>
        <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
  </xsl:template>

  <xsl:template match="properties/*">
     <entry>
         <xsl:attribute name="key" select="name()"/>
         <xsl:apply-templates select="@* | node()"/>
     </entry>
  </xsl:template>

当我尝试使用另一个模板 &lt;xsl:template match="properties[not(contains(name(), 'foo'))]"/&gt; 时,它不会在 'entry' 元素模板之后执行。如果我在入口模板之前做一个模板,入口模板不执行。

期望的输出

<?xml version="1.0" encoding="UTF-8"?>
<properties>
    <entry key="foobar">CCC</entry>
    <entry key="foofoofoobar">EEE</entry>
</properties>

非常感谢您的帮助。

编辑

我能够创建&lt;xsl:template match="entry[not(contains(@*, 'foo'))]"/&gt;,它将删除不包含 foo 的条目元素,这不完全是我想要的,但这是朝着正确方向迈出的又一步。但是,如果我将它们全部放在一个 XSLT 文档中,则只会运行第一个模板。

【问题讨论】:

    标签: xml xslt


    【解决方案1】:

    出于问题的目的,我假设另一个模板看起来像这样

    <xsl:template match="properties[not(contains(name(), 'foo'))]" /> 
    

    这与 properties 元素匹配,并且由于名称“properties”不包含文本“foo”,因此条件为真,因此处理有效地在此处停止(即 properties 不会被复制,并且没有一个子 entry 节点匹配)。

    您可能想要这样做(我已从 contains 换成 starts-with,因为这符合您的要求)

    <xsl:template match="properties/*[not(starts-with(name(), 'foo'))]" /> 
    

    但是....模板优先级存在问题。

    元素properties/bar(例如)将与您的两个模板匹配,因此XSLT 必须调用Conflict Resolution for Template Rules(如果描述看起来太复杂,请查看http://www.lenzconsulting.com/how-xslt-works/#priority)。这意味着您的两个模板具有相同的优先级; 0.5。在这种情况下,通常发生的情况是处理器将使用 XSLT 中最后一个匹配的模板,尽管某些处理器可能会发出错误信号。

    要解决此问题,您可以优先考虑模板

    <xsl:template match="properties/*[not(starts-with(name(), 'foo'))]" priority="0.6" />
    

    或者您可以向其他模板添加条件

    <xsl:template match="properties/*[starts-with(name(), 'foo')]">
    

    或者您可以将两个模板合二为一...

    <xsl:template match="properties/*">
      <xsl:if test="starts-with(name(), 'foo')">
        <entry key="{name()}">
          <xsl:apply-templates select="@* | node()"/>
        </entry>
      </xsl:if>
    </xsl:template>
    

    试试这个 XSLT(它使用优先级方法)

    <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
        xmlns:xs="http://www.w3.org/2001/XMLSchema" exclude-result-prefixes="xs" version="2.0">
    
      <xsl:template match="node()|@*" name="identity">
         <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
         </xsl:copy>
      </xsl:template>
    
      <xsl:template match="properties/*[not(starts-with(name(), 'foo'))]" priority="0.6" />
    
      <xsl:template match="properties/*">
         <entry key="{name()}">
             <xsl:apply-templates select="@* | node()"/>
         </entry>
      </xsl:template>
    </xsl:stylesheet>
    

    作为奖励,此 XSLT 还利用 Attribute Value Templatesentry 元素上创建 key 属性。

    【讨论】:

    • 这太棒了!效果很好。一个问题是,当它删除不以“foo”开头的节点时,节点之间会留下一个空格。如何删除空格?
    • 使用xsl:strip-space去除空白。见xsltfiddle.liberty-development.net/jyH9rMB
    • 完美!感谢您对我的 XSL 的帮助。这看起来很棒!
    猜你喜欢
    • 2017-05-31
    • 1970-01-01
    • 1970-01-01
    • 2011-03-08
    • 1970-01-01
    • 1970-01-01
    • 2017-01-12
    • 2014-04-21
    • 1970-01-01
    相关资源
    最近更新 更多