XML:
<root>
<item>
<key>mustBeSECONDKey</key>
<value>MustBeSECONDValue</value>
</item>
<item>
<key>mustBeFIRSTKey</key>
<value>MustBeFIRSTValue</value>
</item>
</root>
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<file>
<xsl:for-each select="root/item">
<xsl:if test="key[text()='mustBeFIRSTKey']">
<xsl:element name="someCustomTagName">
<xsl:value-of select="value" />
</xsl:element>
</xsl:if>
</xsl:for-each>
<xsl:for-each select="root/item">
<xsl:if test="key[text()='mustBeSECONDKey']">
<xsl:element name="anotherNameOfATag">
<xsl:value-of select="value" />
</xsl:element>
</xsl:if>
</xsl:for-each>
</file>
</xsl:template>
</xsl:stylesheet>
输出:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<file>
<someCustomTagName>MustBeFIRSTValue</someCustomTagName>
<anotherNameOfATag>MustBeSECONDValue</anotherNameOfATag>
</file>
这里的想法是我想确保标签以我指定的顺序结束在输出文档中。所以我通过基本上说“透过一切并找到第一件事,然后查看所有内容并找到第二件事来做到这一点。”这显然有效。
然而 - 这将是这个问题的关键 - 我想必须有一种更有效的方法来实现这个目标(可能不止一种方式)。它是什么?
还有一个皱纹。假设mustBeFIRSTKey
有两个可能的值,MustBeFIRSTValueUno
和MustBeFIRSTValueNi
。然后,我想将这两个值分别映射到另一组两个值Gazpacho
和Sushi
。所以
<item>
<key>mustBeFIRSTKey</key>
<value>MustBeFIRSTValueNi</value>
</item>
变为
<mustBeFIRSTKey>Sushi</mustBeFIRSTKey>
编辑:我发现我的问题主要是一个概念性问题,看看Java方面的问题。在设置Transformer
期间,我正在执行此操作:
StreamSource xsltSource = new StreamSource(ClassLoader.getSystemResourceAsStream(transformLocation));
我应该这样做:
StreamSource xsltSource = new StreamSource(ClassLoader.getSystemResource(transformLocation).toString());
StreamSource
构造函数设置systemId
,这允许我从下面使用@ Ian_Robert更聪明的解决方案,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!-- I discovered I could merge the $map and $inlineMap variables. -->
<xsl:variable name="inlineMap" select="document('')//xsl:variable[@name = 'inlineMap']/map">
<map>
<key from="mustBeFIRSTKey" to="someCustomTagName" />
<key from="mustBeSECONDKey" to="anotherNameOfATag">
<value from="MustBeSECONDValueUno" to="Gazpacho" />
<value from="MustBeSECONDValueNi" to="Sushi" />
</key>
</map>
</xsl:variable>
<xsl:key name="valueMap" match="value" use="concat(../@from, '|', @from)" />
<xsl:template match="root">
<xsl:variable name="items" select="item" />
<file>
<xsl:for-each select="$inlineMap">
<xsl:for-each select="key">
<xsl:apply-templates select="$items[key = current()/@from]">
<xsl:with-param name="elementName" select="@to"/>
</xsl:apply-templates>
</xsl:for-each>
</xsl:for-each>
</file>
</xsl:template>
<xsl:template match="item">
<xsl:param name="elementName" />
<xsl:variable name="currentItem" select="." />
<xsl:element name="{$elementName}">
<xsl:for-each select="$inlineMap">
<xsl:variable name="value" select="key('valueMap', concat($currentItem/key, '|', $currentItem/value))" />
<xsl:choose>
<xsl:when test="$value">
<xsl:value-of select="$value/@to" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$currentItem/value" />
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
这个例子删除了处理值的逻辑而没有映射,因为对于我的问题,没有映射。
答案 0 :(得分:2)
您可以使用<xsl:sort>
来实现此目的
<xsl:variable name="sortOrder" select="'|MustBeFIRSTKey|MustBeSECONDKey|--' />
<xsl:template match="/">
<file>
<xsl:apply-templates select="root/item">
<xsl:sort select="string-length(substring-after($sortOrder,
concat('|', key, '|')))"
data-type="number"
order="descending" />
</xsl:apply-templates>
</file>
</xsl:template>
<xsl:template match="item">
<xsl:element name="{key}">
<xsl:value-of select="value" />
</xsl:element>
</xsl:template>
这将首先放入MustBeFIRSTKey,将MustBeSECONDKey放在第二位,然后按原始顺序放置此后的任何其他键值。它的工作原理是根据每个键在sortOrder
变量中的位置为其生成一个数字排序值。对于MustBeFIRSTKey,值为string-length('MustBeSECONDKey|--')
(18),对于MustBeSECONDKey,它将为string-length('--')
(2),对于任何其他内容,它将为string-length('')
(0)。
要更改标记名称,您可以扩展此方法以使用变量对映射进行编码:
<xsl:variable name="sortOrder" select="concat(
'|MustBeFIRSTKey+someCustomTagName',
'|MustBeSECONDKey+anotherNameOfATag',
'|--' />
<xsl:template match="/">
<file>
<xsl:apply-templates select="root/item">
<xsl:sort select="string-length(substring-after($sortOrder,
concat('|', key, '+')))"
data-type="number"
order="descending" />
</xsl:apply-templates>
</file>
</xsl:template>
<!-- for keys that have a mapping -->
<xsl:template match="item[substring-after($sortOrder, concat('|', key, '+'))]">
<xsl:element name="{substring-before(
substring-after($sortOrder, concat('|', key, '+')), '|')}">
<xsl:value-of select="value" />
</xsl:element>
</xsl:template>
<!-- for keys that don't -->
<xsl:template match="item">
<xsl:element name="{key}">
<xsl:value-of select="value" />
</xsl:element>
</xsl:template>
编辑:对于你的第三个问题,如果你想重新映射值和键,那么我认为一个单独的映射文件可能是最清晰的方法。
<强> mapping.xml 强>
<map>
<key from="MustBeFIRSTKey" to="someCustomTagName">
<value from="MustBeFIRSTValueUno" to="Gazpacho" />
<value from="MustBeFIRSTValueNi" to="Sushi" />
</key>
<key from="MustBeSECONDKey" to="anotherNameOfATag" />
</map>
<强>样式表强>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="map" select="document('mapping.xml')/map" />
<!-- define some keys used for looking up entries in the mapping -->
<xsl:key name="keyMap" match="key" use="@from" />
<xsl:key name="valMap" match="value" use="concat(../@from, '|', @from)" />
<xsl:template match="/">
<xsl:variable name="items" select="root/item" />
<file>
<!-- the key function looks up nodes in the "current document", so
we need this for-each to switch the context to the mapping file
for the key lookups -->
<xsl:for-each select="$map">
<!-- go through the keys in order -->
<xsl:for-each select="key">
<!-- process all items with that key... -->
<xsl:apply-templates select="$items[key = current()/@from]">
<!-- ... using the mapped tag name -->
<xsl:with-param name="tagName" select="@to"/>
</xsl:apply-templates>
</xsl:for-each>
<!-- now process any remaining keys that don't have a mapping -->
<xsl:apply-templates select="$items[not(key('keyMap', key))]" />
</xsl:for-each>
</file>
</xsl:template>
<xsl:template match="item">
<!-- tag name defaults to the key text if another name is not passed in -->
<xsl:param name="tagName" select="key" />
<xsl:variable name="curItem" select="." />
<xsl:element name="{$tagName}">
<!-- again, switch focus to the mapping file for key lookups -->
<xsl:for-each select="$map">
<!-- do we have a remapping for the item's *value*? -->
<xsl:variable name="value"
select="key('valMap', concat($curItem/key, '|', $curItem/value))" />
<xsl:choose>
<xsl:when test="$value"><xsl:value-of select="$value/@to" /></xsl:when>
<xsl:otherwise><xsl:value-of select="$curItem/value" /></xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>
如果映射文件足够小,您可以通过替换
将其包含在样式表中 <xsl:variable name="map" select="document('mapping.xml')/map" />
与
<xsl:variable name="inlineMap">
<map>
<key from="..." to="..."/>
<!-- ... -->
</map>
</xsl:variable>
<xsl:variable name="map"
select="document('')//xsl:variable[@name='inlineMap']/map" />
这使用了document('')
使您可以访问样式表本身的XML树的技巧。
最后,请注意keyMap
仅用于查找不的键在映射文件中有条目的项目。如果您拥有所有可能键的映射,则可以保留此键(以及使用它的<xsl:apply-templates select="$items[not(key('keyMap', key))]" />
)。
答案 1 :(得分:1)
我首先要写一个模板
<xsl:template match="item">
<xsl:element name="{key}">
<xsl:value-of select="value" />
</xsl:element>
</xsl:template>
现在你可以使用
<xsl:template match="/root">
<file>
<xsl:apply-templates select="item[key ='MustBeFIRSTKey']"/>
<xsl:apply-templates select="item[key = 'MustBeSECONDKey']"/>
</file>
</xsl:template>
使用XSLT 2.0,您只需要
<xsl:template match="/root">
<file>
<xsl:apply-templates select="item[key ='MustBeFIRSTKey'], item[key = 'MustBeSECONDKey']"/>
</file>
</xsl:template>
当然,无论是XSLT 1.0还是2.0,您都可以定义一个密钥
<xsl:key name="k1" match="item" use="key"/>
然后将代码缩短为
<xsl:template match="/root">
<file>
<xsl:apply-templates select="key('k1', 'MustBeFIRSTKey')"/>
<xsl:apply-templates select="key('k1', 'MustBeSECONDKey')"/>
</file>
</xsl:template>
分别
<xsl:template match="/root">
<file>
<xsl:apply-templates select="key('k1', 'MustBeFIRSTKey'), key('k1', 'MustBeSECONDKey')"/>
</file>
</xsl:template>
当然,根据您的预期排序,您可以使用xsl:sort
而不是拼写订单。
答案 2 :(得分:1)
我会让这个数据驱动。在单独的XML文件中定义项目的顺序,可以是单独的文档,也可以是样式表的一部分:
<order>
<item>mustbefirst</item>
<item>mustbesecond</item>
</order>
并使用它来驱动样式表处理:
<xsl:template match="order">
<xsl:for-each select="item">
<xsl:variable name="target" select="$root/item[key=current()]">
<xsl:if test="$target">
<xsl:element name="$target/key"><xsl:value-of select="$target/value"/></xsl:element>
</xsl:if>
</xsl:for-each>
</xsl:template>
其中$ root绑定到你的&#34; root&#34;元件。