Xml 如何使用xslt填充文本模板
我有一个包含信息的XML文件,例如:Xml 如何使用xslt填充文本模板,xml,xslt,replace,Xml,Xslt,Replace,我有一个包含信息的XML文件,例如: <letter> <name>Test</name> <age>20</age> <me>Me</me> </letter> 当使用xslt将XML转换为纯文本字母时,我可以使用如下内容: <xsl:text>Dear </xsl:text><xsl:value-of select="name"/><xsl:
<letter>
<name>Test</name>
<age>20</age>
<me>Me</me>
</letter>
当使用xslt将XML转换为纯文本字母时,我可以使用如下内容:
<xsl:text>Dear </xsl:text><xsl:value-of select="name"/><xsl:text>
some text with other variables like </xsl:text>
<xsl:value-of select="age"/><xsl:text> or </xsl:text>
<xsl:value-of select="name"/><xsl:text> again
greatings </xsl:text><xsl:value-of select="me"/>
亲爱的
一些文本包含其他变量,如
或
再一次
格雷廷
但是,当我得到越来越多的变量和更多的文本时,这就变成了输入和维护的噩梦
是否有某种方法可以使用xslt以更干净的方式实现这一点?如果我可以使用上面作为示例的文本模板,并将$name和$age替换为正确的值,我会更愿意这样做。您可以这样做:
<xsl:stylesheet
version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>
<xsl:output method="text" />
<xsl:variable name="placeholderText"
>Dear $name,
some text with other variables like $age or $name again,
the $undefined will not be replaced.
greatings $me</xsl:variable>
<xsl:template match="letter">
<xsl:call-template name="expand-placeholders">
<xsl:with-param name="text" select="$placeholderText" />
<xsl:with-param name="values" select="*" />
</xsl:call-template>
</xsl:template>
<xsl:template name="expand-placeholders">
<xsl:param name="text" select="''" />
<xsl:param name="values" select="false()" />
<xsl:choose>
<xsl:when test="contains($text, '$') and $values">
<xsl:variable name="head" select="substring-before($text, '$')" />
<xsl:variable name="curr" select="substring-after($text, '$')" />
<!-- find the longest matching value name... -->
<xsl:variable name="valName">
<xsl:for-each select="$values[starts-with($curr, name())]">
<xsl:sort select="string-length(name())" data-type="number" order="descending" />
<xsl:if test="position() = 1">
<xsl:value-of select="name()" />
</xsl:if>
</xsl:for-each>
</xsl:variable>
<!-- ... and select the appropriate placeholder element -->
<xsl:variable name="val" select="$values[name() = $valName][1]" />
<xsl:variable name="tail">
<xsl:choose>
<xsl:when test="$val">
<xsl:value-of select="substring-after($curr, name($val))" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$curr" />
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:value-of select="$head" />
<xsl:if test="not($val)">$</xsl:if>
<xsl:value-of select="$val" />
<xsl:call-template name="expand-placeholders">
<xsl:with-param name="text" select="$tail" />
<xsl:with-param name="values" select="$values" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$text" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
亲爱的$name,
某些文本包含其他变量,如$age或$name,
未定义的$undefined将不会被替换。
给我一美元
$
针对XML示例的输出:
Dear Test,
some text with other variables like 20 or Test again,
the $undefined will not be replaced.
greatings Me
亲爱的测试,
一些文本包含其他变量,如20或再次测试,
未定义的$undefined将不会被替换。
使我变大
正如@Mads-Hansen所建议的,如果您可以使用基于XML的模板,那么这可以以一种更简单、更好的方式解决 下面是一个以XML模板作为输入的解决方案 作为输入的XML模板:
<?xml version="1.0" encoding="UTF-8"?>
<letter>Dear <name/>,
some text with other variables like <age/> or <name/> again
greatings <me/></letter>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vars="my.variables">
<xsl:output method="text"/>
<xsl:preserve-space elements="letter"/>
<vars:letter>
<name>Test</name>
<age>20</age>
<me>Me</me>
</vars:letter>
<xsl:template match="name|age|me">
<xsl:variable name="name" select="local-name()"/>
<xsl:value-of select="document('')/*/vars:letter/*[local-name() = $name]"/>
</xsl:template>
</xsl:stylesheet>
Dear Test,
some text with other variables like 20 or Test again
greatings Me
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vars="my.variables">
<xsl:output method="text"/>
<xsl:preserve-space elements="letter"/>
<vars:letter>
<name>Test</name>
<age>20</age>
<me>Me</me>
</vars:letter>
<xsl:template match="letter">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="letter/*">
<xsl:variable name="name" select="local-name()"/>
<xsl:variable name="content" select="document('')/*/vars:letter/*[local-name() = $name]"/>
<xsl:value-of select="$content"/>
<xsl:if test="not($content)">
<xsl:message>
<xsl:text>Found unknown variable in template: </xsl:text>
<xsl:value-of select="concat('<', local-name(), '/>')" disable-output-escaping="yes"/>
</xsl:message>
<xsl:value-of select="concat('<', local-name(), '/>')"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
如果使用XML模板作为输入,这是一种可能的解决方法。当然,可以使用fn:document()
<xsl:value-of select="document('path/to/file.xml')/letter/*[local-name() = $name]"/>
更新:正如@Tomlak评论的那样,上述解决方案不是很灵活,因此这里有一个更新的解决方案:
XSLT:
<?xml version="1.0" encoding="UTF-8"?>
<letter>Dear <name/>,
some text with other variables like <age/> or <name/> again
greatings <me/></letter>
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vars="my.variables">
<xsl:output method="text"/>
<xsl:preserve-space elements="letter"/>
<vars:letter>
<name>Test</name>
<age>20</age>
<me>Me</me>
</vars:letter>
<xsl:template match="name|age|me">
<xsl:variable name="name" select="local-name()"/>
<xsl:value-of select="document('')/*/vars:letter/*[local-name() = $name]"/>
</xsl:template>
</xsl:stylesheet>
Dear Test,
some text with other variables like 20 or Test again
greatings Me
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:vars="my.variables">
<xsl:output method="text"/>
<xsl:preserve-space elements="letter"/>
<vars:letter>
<name>Test</name>
<age>20</age>
<me>Me</me>
</vars:letter>
<xsl:template match="letter">
<xsl:apply-templates/>
</xsl:template>
<xsl:template match="letter/*">
<xsl:variable name="name" select="local-name()"/>
<xsl:variable name="content" select="document('')/*/vars:letter/*[local-name() = $name]"/>
<xsl:value-of select="$content"/>
<xsl:if test="not($content)">
<xsl:message>
<xsl:text>Found unknown variable in template: </xsl:text>
<xsl:value-of select="concat('<', local-name(), '/>')" disable-output-escaping="yes"/>
</xsl:message>
<xsl:value-of select="concat('<', local-name(), '/>')"/>
</xsl:if>
</xsl:template>
</xsl:stylesheet>
试验
20
我
在模板中找到未知变量:
请注意,这是一种不同的方法,因为它使用模板作为输入文档,而不是变量列表。为什么?对我来说,使用字母模板作为输入更有意义,因为它实际上是您转换并作为输出获取的文档。一种可能的解决方案是将模板文件更改为XML配置,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<template>
Dear <name/>,
some text with other variables like <age/> or <name/> again
greatings <me/>
</template>
<!--Match on any of the template placeholder elements and replace with the value from it's corresponding letter document-->
<xsl:template match="template/*">
<!--set the local-name of the current element as a variable, so that we can use it in the expression below -->
<xsl:variable name="templateElementName" select="local-name(.)" />
<!--Find the corresponding letter element that matches this template element placeholder-->
<xsl:variable name="replacementValue" select="$letter/*[local-name()=$templateElementName]" />
<xsl:choose>
<xsl:when test="$replacementValue">
<xsl:value-of select="$replacementValue" />
</xsl:when>
<xsl:otherwise>
<xsl:text>$</xsl:text>
<xsl:value-of select="local-name()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
对示例XML文件运行XSLT时,它会生成以下输出:
Dear Test,
some text with other variables like 20 or Test again
greatings Me
正如@Tomalak指出的,不匹配的占位符元素将从输出中删除。如果要保留它们,为了使XML文件明显与模板中的占位符项不匹配,可以更改与模板占位符元素匹配的模板,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<template>
Dear <name/>,
some text with other variables like <age/> or <name/> again
greatings <me/>
</template>
<!--Match on any of the template placeholder elements and replace with the value from it's corresponding letter document-->
<xsl:template match="template/*">
<!--set the local-name of the current element as a variable, so that we can use it in the expression below -->
<xsl:variable name="templateElementName" select="local-name(.)" />
<!--Find the corresponding letter element that matches this template element placeholder-->
<xsl:variable name="replacementValue" select="$letter/*[local-name()=$templateElementName]" />
<xsl:choose>
<xsl:when test="$replacementValue">
<xsl:value-of select="$replacementValue" />
</xsl:when>
<xsl:otherwise>
<xsl:text>$</xsl:text>
<xsl:value-of select="local-name()"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
$
例如,如果存在不匹配的占位符元素,
,则它将在文本输出中显示为$foo
此样式表:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my">
<xsl:output method="text"/>
<xsl:preserve-space elements="my:layout"/>
<my:layout>Dear <name/>,
some text with other variables like <age/> or <name/> again
greatings <me/></my:layout>
<xsl:variable name="vData" select="/"/>
<xsl:template match="/">
<xsl:apply-templates select="document('')/*/my:layout/node()"/>
</xsl:template>
<xsl:template match="*/*">
<xsl:value-of select="$vData//*[name()=name(current())]"/>
</xsl:template>
</xsl:stylesheet>
注意:对于更复杂的总体模式(即迭代),请检查此帖子:文本文件必须是文本吗?您是否愿意将其更改为XML模板文件?这将使它更容易操纵。另外,XSLT 2.0是您的选择,还是您需要XSLT 1.0解决方案?@Mads Hansen:输出必须是文本,实际模板可以是任何易于理解的内容。XSLT 2.0不受支持,但大部分EXSLT都受支持。这是一种紧凑但不灵活的方法,因为每次创建新变量时都必须更改源代码。@Tomalak:这是对的,但它满足了他的要求。有关更通用的解决方案,请参阅更新的样式表。仍然不灵活,因为我认为占位符变量是…well变量。:-)同样,从不同的文档加载它们也很容易,而不是在样式表中使用自定义的namecspace元素,所以从我这里得到+1。@Tomlak:是的,这就是想法。将文档(“”)
更改为使用外部文档作为占位符变量将使代码更易于维护。+1。但是,占位符模板不应接触未知元素IMHO。目前它正在删除它们。嗯,占位符元素在序列化为XML时会被“吃掉”,因为它们是空的。可能被视为一个功能或一个bug。我将添加一个如何保留不匹配元素的示例。+1使用给定的输入生成所需的输出。唯一需要注意的是,starts-with()
可能会生成一些假阳性匹配(如果有名为country
和countryCode
的占位符),但对于给定的示例数据非常有效。@Mads Hansen:是的,我已经考虑过了。$val
的选择器应更改为选择具有最长名称的元素。@Mads Hansen:我已做了适当的更改,以使用最长的匹配占位符名称,而不是第一个匹配的占位符名称。现在将正确区分$country
和$countryCode
。+1表示实际使用$name占位符。不过,我更喜欢使用XML标记的另一个解决方案。我喜欢这个,非常简短。谢谢@瑞夫:你很好!另外,看看这些链接。