是dyn:评估已知在Xalan-J 2.7.1中存在问题的扩展函数吗?

时间:2017-01-23 15:06:21

标签: java xslt xpath xalan exslt

我很难过为什么下面的示例程序拒绝正确应用我的样式表。似乎Xalan 2.7.1中的dyn:evaluate拒绝处理某些XPath变量。

在类路径中使用xalan-j运行以下程序会产生以下结果:

package com.testing2.xslt;

import java.io.*;
import java.util.logging.*;
import javax.xml.transform.*;
import javax.xml.transform.stream.*;

public class DynEvaluateTransform {

    private static final String XSLT = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
"    xmlns:det=\"org:example:DynEvaluateTransform\"\n" +
"    xmlns:dyn=\"http://exslt.org/dynamic\"\n" +
"    extension-element-prefixes=\"dyn\"\n" +
"    version=\"1.0\">\n" +
"    \n" +
"    <xsl:variable name=\"input-doc\" select=\"document('input.xml', /)\" />\n" +
"    <xsl:variable name=\"selections\" select=\"$input-doc/det:selections\" />\n" +
"    \n" +
"    <xsl:template match=\"/\">\n" +
"        <xsl:choose>\n" +
"            <xsl:when test=\"function-available('dyn:evaluate')\">\n" +
"                <xsl:message>dyn:evaluate available</xsl:message>\n" +
"            </xsl:when>\n" +
"            <xsl:otherwise>\n" +
"                <xsl:message>dyn:evaluate not available</xsl:message>\n" +
"            </xsl:otherwise>\n" +
"        </xsl:choose>\n" +
"        <xsl:message>input.xml content:</xsl:message>\n" +
"        <xsl:for-each select=\"$selections/*\">\n" +
"            <xsl:message>{<xsl:value-of select=\"namespace-uri()\"/>}<xsl:value-of select=\"local-name()\"/></xsl:message>\n" +
"        </xsl:for-each>        \n" +
"        <xsl:for-each select=\"//@condition\">\n" +
"            <xsl:message><xsl:value-of select=\".\"/></xsl:message>\n" +
"            <xsl:message><xsl:value-of select=\"string(dyn:evaluate(string(.)))\"/></xsl:message>\n" +
"        </xsl:for-each>\n" +
"    </xsl:template>\n" +
"    \n" +
"</xsl:stylesheet>";

    private static final String EXAMPLE = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<foos xmlns=\"org:example:DynEvaluateTransform\">    \n" +
"    <foo condition=\"true()\" />    \n" +
"    <foo condition=\"false()\" />    \n" +
"    <foo condition=\"false() or true()\" />\n" +
"    <foo condition=\"$selections\" />\n" +
"    <foo condition=\"$selections/*\" />\n" +
"    <foo condition=\"$selections/*[local-name()='a']\" />\n" +
"    <foo condition=\"$selections/element::node()[local-name()='a']\" />\n" +
"    <foo condition=\"local-name($selections/*)\" />\n" +
"    <foo condition=\"not($selections/*[local-name()='a' and namespace-uri()='org:example:foo'])\" />\n" +
"    <foo condition=\"$selections/*[local-name()='b' and namespace-uri()='org:example:foo']\" />\n" +
"    <foo condition=\"$selections/*[local-name()='c' and namespace-uri()='org:example:foo']\" />\n" +
"    <foo condition=\"not($selections/*[local-name()='a' and namespace-uri()='org:example:foo']) or ($selections/*[local-name()='b' and namespace-uri()='org:example:foo'] and $selections/*[local-name()='c' and namespace-uri()='org:example:foo'])\" />\n" +
"</foos>";

    private static final String INPUT = "" +
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<selections xmlns=\"org:example:DynEvaluateTransform\">\n" +
"    <a xmlns=\"org:example:foo\"/>\n" +
"    <b xmlns=\"org:example:foo\"/>\n" +
"    <c xmlns=\"org:example:foo\"/>\n" +
"</selections>";

    private TransformerFactory xalanTransFact;    

    public DynEvaluateTransform() {
        xalanTransFact = new org.apache.xalan.processor.TransformerFactoryImpl();
        xalanTransFact.setURIResolver(new Resolver());
    }

    private void applyTransform() {
        // XSLT(EXAMPLE) --> output.xml
        //         ^
        //         |
        //       INPUT
        OutputStreamWriter writer = null;

        try {
            String outputFileName = DynEvaluateTransform.getLocalFileName("output.xml");

            File file = new File(outputFileName);
            writer = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");

            System.out.println(org.apache.xalan.Version.getVersion());

            Transformer transformer = xalanTransFact.newTransformer(new StreamSource(new StringReader(XSLT)));
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");

            transformer.transform(
                    new StreamSource(new StringReader(EXAMPLE)),
                    new StreamResult(writer));

        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException ex) {
                }
            }
        }
    }

    private void rebuildInput() {
        // ignore - this just writes the input.xml file, so we can later reference it
        StringReader strReader = null;
        OutputStreamWriter fileWriter = null;
        try {
            String fileName = getLocalFileName("input.xml");

            strReader = new StringReader(INPUT);
            File file = new File(fileName);
            fileWriter = new OutputStreamWriter(new FileOutputStream(file), "UTF-8");

            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty(OutputKeys.METHOD, "xml");

            transformer.transform(
                    new StreamSource(strReader),
                    new StreamResult(fileWriter));

        } catch (UnsupportedEncodingException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerConfigurationException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (TransformerException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(DynEvaluateTransform.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            if (strReader != null) {
                strReader.close();
            }
            if (fileWriter != null) {
                try {
                    fileWriter.close();
                } catch (IOException ex) {
                }
            }
        }
    }

    public static void main(String[] args) {
        DynEvaluateTransform det = new DynEvaluateTransform();
        det.rebuildInput();
        det.applyTransform();
    }   

    private static String getLocalFileName(String href) {
        String name = System.getProperty("user.dir");
        if (!name.endsWith(File.separator)) {
            name += File.separator;
        }
        name += href;
        return name;
    }

    private static class Resolver implements URIResolver {

        @Override
        public Source resolve(String href, String base) throws TransformerException {
            if ("input.xml".equals(href)) {                
                return new StreamSource(new File(DynEvaluateTransform.getLocalFileName(href)));
            } else {
                return null;
            }
        }
    }
}
Xalan Java 2.7.1
SystemId Unknown; Line #14; Column #30; dyn:evaluate available
SystemId Unknown; Line #20; Column #22; input.xml content:
SystemId Unknown; Line #22; Column #26; {org:example:foo}a
SystemId Unknown; Line #22; Column #26; {org:example:foo}b
SystemId Unknown; Line #22; Column #26; {org:example:foo}c
SystemId Unknown; Line #25; Column #26; true()
SystemId Unknown; Line #26; Column #26; true
SystemId Unknown; Line #25; Column #26; false()
SystemId Unknown; Line #26; Column #26; false
SystemId Unknown; Line #25; Column #26; false() or true()
SystemId Unknown; Line #26; Column #26; true
SystemId Unknown; Line #25; Column #26; $selections
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; $selections/*
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; $selections/*[local-name()='a']
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; $selections/element::node()[local-name()='a']
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; local-name($selections/*)
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; not($selections/*[local-name()='a' and namespace-uri()='org:example:foo'])
SystemId Unknown; Line #26; Column #26; true
SystemId Unknown; Line #25; Column #26; $selections/*[local-name()='b' and namespace-uri()='org:example:foo']
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; $selections/*[local-name()='c' and namespace-uri()='org:example:foo']
SystemId Unknown; Line #26; Column #26; 
SystemId Unknown; Line #25; Column #26; not($selections/*[local-name()='a' and namespace-uri()='org:example:foo']) or ($selections/*[local-name()='b' and namespace-uri()='org:example:foo'] and $selections/*[local-name()='c' and namespace-uri()='org:example:foo'])
SystemId Unknown; Line #26; Column #26; true

示例程序使用提供的转换(XSLT)转换示例输入(EXAMPLE),该转换采用通过input.xml函数打开的输入文件(INPUT,document())作为“参数” 。此输入文件包含一组使用XPath表达式(位于EXAMPLE中)测试的元素。

程序的输出表明支持dyn:evaluate函数,正确读取input.xml,正确评估简单的XPath表达式,但一旦涉及XPath变量,它就会中断。

以下所涉及的所有文件的可读版本。

input.xml中

<?xml version="1.0" encoding="UTF-8"?>
<selections xmlns="org:example:DynEvaluateTransform">
    <a xmlns="org:example:foo"/>
    <b xmlns="org:example:foo"/>
    <c xmlns="org:example:foo"/>
</selections>

实施例

<?xml version="1.0" encoding="UTF-8"?>
<foos xmlns="org:example:DynEvaluateTransform">    
    <foo condition="true()" />    
    <foo condition="false()" />    
    <foo condition="false() or true()" />
    <foo condition="$selections" />
    <foo condition="$selections/*" />
    <foo condition="$selections/*[local-name()='a']" />
    <foo condition="$selections/element::node()[local-name()='a']" />
    <foo condition="local-name($selections/*)" />
    <foo condition="not($selections/*[local-name()='a' and namespace-uri()='org:example:foo'])" />
    <foo condition="$selections/*[local-name()='b' and namespace-uri()='org:example:foo']" />
    <foo condition="$selections/*[local-name()='c' and namespace-uri()='org:example:foo']" />
    <foo condition="not($selections/*[local-name()='a' and namespace-uri()='org:example:foo']) or ($selections/*[local-name()='b' and namespace-uri()='org:example:foo'] and $selections/*[local-name()='c' and namespace-uri()='org:example:foo'])" />
</foos>

XSLT

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:det="org:example:DynEvaluateTransform"
    xmlns:dyn="http://exslt.org/dynamic"
    extension-element-prefixes="dyn"
    version="1.0">

    <xsl:variable name="input-doc" select="document('input.xml', /)" />
    <xsl:variable name="selections" select="$input-doc/det:selections" />

    <xsl:template match="/">
        <xsl:choose>
            <xsl:when test="function-available('dyn:evaluate')">
                <xsl:message>dyn:evaluate available</xsl:message>
            </xsl:when>
            <xsl:otherwise>
                <xsl:message>dyn:evaluate not available</xsl:message>
            </xsl:otherwise>
        </xsl:choose>
        <xsl:message>input.xml content:</xsl:message>
        <xsl:for-each select="$selections/*">
            <xsl:message>{<xsl:value-of select="namespace-uri()"/>}<xsl:value-of select="local-name()"/></xsl:message>
        </xsl:for-each>        
        <xsl:for-each select="//@condition">
            <xsl:message><xsl:value-of select="."/></xsl:message>
            <xsl:message><xsl:value-of select="string(dyn:evaluate(string(.)))"/></xsl:message>
        </xsl:for-each>
    </xsl:template>

</xsl:stylesheet>

Xalan不支持dyn:evaluate参数中的XPath变量吗?我是否正确定义了表达式?尝试在选择Xalan的情况下在oXygen中运行此样式表,在首次遇到XPath变量时报告java.lang.RuntimeException: ElemTemplateElement error: Function not supported!

编辑:

我已经重写了这个问题,以澄清我的实际问题是什么。

1 个答案:

答案 0 :(得分:0)

正如所担心的那样,Xalan 2.7.1中的dyn:evaluate似乎不支持XPath变量中的结果树片段。请注意,具有非结果树片段值的变量可以正常工作。

我编写了自己的扩展函数(dyn:evaluate等价物)来评估XPath表达式,但它也无法处理变量中的结果树片段(收到this method is not yet supported例外)。这可能是JAXP提供的XPath引擎实现的限制。

也许它适用于不同的XPath引擎实现。

我放弃尝试完成这项工作,并避免在dyn:evaluate正在运行时将结果树片段填充到XPath变量中。我将简单地提供通过xsl:param字符串值来评估我的条件所需的信息,而不是使用外部XML文件的困难方法。 Xalan的扩展函数将检查选择是否实际存在。