Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/xml/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/xslt/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Xml 如何使用xslt填充文本模板_Xml_Xslt_Replace - Fatal编程技术网

Xml 如何使用xslt填充文本模板

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:

我有一个包含信息的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: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('&lt;', local-name(), '/&gt;')" disable-output-escaping="yes"/>
      </xsl:message>
      <xsl:value-of select="concat('&lt;', local-name(), '/&gt;')"/>
    </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('&lt;', local-name(), '/&gt;')" disable-output-escaping="yes"/>
      </xsl:message>
      <xsl:value-of select="concat('&lt;', local-name(), '/&gt;')"/>
    </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标记的另一个解决方案。我喜欢这个,非常简短。谢谢@瑞夫:你很好!另外,看看这些链接。