我正在尝试创建一个使用以下步骤转换 XML 的 XSL:
- 确定根“properties”下的元素是否具有以“foo”开头的名称。如果它不以“foo”开头,则删除该元素。
- 创建名为“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>
当我尝试使用另一个模板时<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 文档中,则只有第一个模板会运行。
最佳答案
出于问题的目的,我假设另一个模板看起来像这样
<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 Templates在 entry
元素上创建 key
属性。
关于xml - 具有多个模板的 XSL,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51858595/