XSLT document with dynamic path

2019-08-27 14:07发布

I have XSLT 1.0 standard. I have one dynamic XML from server which is quite simple and second XML as configuration. Base on first one (which is dynamic) I have to pick up proper nodes information from second one. This is first document:

<?xml version="1.0" encoding="UTF-8" ?>
<response>
    <response>SUCCESS</response>
    <responsedata>
        <hit>
            <url>http://domain.com/page.html</url>
            <id>2437</id>
            <title>Page title</title>
            <language>en</language>
            ...
            ...
        </hit>
    </responsedata>
</response>

Second configuration XML is for footer, header divided by languages. Something like that:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <set id="local">
        <header>
            <en>
                <![CDATA[
<div id="header">
    <p>English code</p>
</div>
                ]]>
            </en>
            <fr>
                <![CDATA[
<div id="header">
    <p>French code</p>
</div>
                ]]>
            </fr>
        </header>
    </set>
</config>

I need pick up proper language depended code from second XML. I tried following code and it doesn't work:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:output method="html" omit-xml-declaration="yes" encoding="utf-8" indent="yes" doctype-system="about:legacy-compat" />
    <xsl:variable name="configuration" select="document('settings.xml')/config/set[@id='local']" />
    <xsl:variable name="lang" select="response/responsedata/hit/language" />

    <xsl:template name="getvalueofnode">
        <xsl:param name="path" />
        <xsl:param name="context" select="$configuration" />
        <xsl:choose>
            <xsl:when test="contains($path,'/')">
                <xsl:call-template name="getvalueofnode">
                    <xsl:with-param name="path"    
                        select="substring-after($path,'/')" />
                    <xsl:with-param name="context" 
                        select="$context/*[name()=substring-before($path,'/')]" />
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <p>value: <xsl:value-of select="$context/*[name()=$path]" disable-output-escaping="yes" /></p>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="/">
        <xsl:element name="html">
            <xsl:attribute name="lang"><xsl:value-of select="$lang" /></xsl:attribute>
            <xsl:element name="head">
                <xsl:attribute name="lang"><xsl:value-of select="$lang" /></xsl:attribute>
            </xsl:element>
            <xsl:element name="body">
                <xsl:attribute name="lang"><xsl:value-of select="$lang" /></xsl:attribute>
                <p>lang: <xsl:value-of select="$lang" /></p>
                <p>
                <xsl:call-template name="getvalueofnode">
                    <xsl:with-param name="path" select="concat('/header/',$lang)" />
                </xsl:call-template>
                </p>
            </xsl:element>
        </xsl:element>
    </xsl:template>
</xsl:stylesheet>

If some one has any suggestion or solution it will be fantastic.

2条回答
Emotional °昔
2楼-- · 2019-08-27 14:20

I'm not too sure if I got your problem correctly however I'll give it a try nevertheless.

When you try to get your value with the getvalueofnode template you are supplying /header/en as path. The template will the chop off the string before the first slash and look for an element with that name in your configuration. It happens to be that the chopped off value is the empty string which results in your template looking for an element in the configuration without a name (that does not exist).

$content will therefore be an empty result tree fragment in the second call (first recursive call). Because this doesn't contain a slash your template print the value that is not there and therefore an empty result tree fragment as well. Since empty tree fragments are converted to an empty string when printed your output will just be:

<p>value: </p>

Regarding a solution: Just remove the leading slash when calling your template for the first time. That's use:

<xsl:call-template name="getvalueofnode">
    <xsl:with-param name="path" select="concat('header/',$lang)" />
</xsl:call-template>

Your transformation will then result in the English header.

By the way your call to getvalueofnode is encapsulated in <p> and so is the place where it returns its value. This leads to nested paragraph tags in the final output.

查看更多
乱世女痞
3楼-- · 2019-08-27 14:36

Since you already know the name of the element based on $lang, you can eliminate the getvalueofnode template all together.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="html" omit-xml-declaration="yes" encoding="utf-8" indent="yes" doctype-system="about:legacy-compat" />
  <xsl:variable name="configuration" select="document('settings.xml')/config/set[@id='local']" />
  <xsl:variable name="lang" select="response/responsedata/hit/language" />

  <xsl:template match="/">
    <html lang="{$lang}">
      <head lang="{$lang}"/>
      <body lang="{$lang}">
        <p>lang: <xsl:value-of select="$lang" /></p>
        <p>value: <xsl:value-of disable-output-escaping="yes" select="$configuration/header/*[name()=$lang]"/></p>
      </body>
    </html>
  </xsl:template>

</xsl:stylesheet>

I also got rid of all the xsl:element and xsl:attribute. These can normally be avoided by coding the elements directly and using AVT (attribute value templates) for the attributes.

The stylesheet above produces the following output using your XML input files (tested with Saxon 6.5.5 and Saxon-HE 9.3.0.5):

<!DOCTYPE html
  SYSTEM "about:legacy-compat">
<html lang="en">
   <head lang="en">
      <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
   </head>
   <body lang="en">
      <p>lang: en</p>
      <p>value: 

         <div id="header">
         <p>English code</p>
         </div>


      </p>
   </body>
</html>
查看更多
登录 后发表回答