具有多个模板的XSL

时间:2018-08-15 12:20:24

标签: xml xslt

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

  1. 标识根“ properties”下的元素是否具有以“ foo”开头的名称。如果它不是以'foo'开头,则删除该元素。
  2. 使用属性“ key”创建名为“ entry”的新元素,该属性等于以“ 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>

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

所需的输出

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

非常感谢您的帮助。

编辑

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

1 个答案:

答案 0 :(得分:1)

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

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

这与properties元素匹配,并且因为名称“ properties”不包含文本“ foo”,所以条件为true,因此处理实际上在此处停止(即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 Templateskey元素上创建entry属性。