使用Java-Extension将Xalan替换为Saxon for XSL

时间:2015-08-19 10:32:05

标签: xslt saxon xalan

我在Xalan的业务流程中有几个XSL转换,其中一个步骤生成带有静态Java扩展函数的XSL-Stylesheets。

我想用Saxon取代Xalan(不仅是因为性能问题,也是因为XSLT2的使用)。

我知道在XSL中需要改变什么才能让Saxon使用这些功能。并且它运行良好(使用Xalan优化的XSL,我获得了40的加速,只有一半使用RAM)。

我的问题是这些生成的XSL-Stylesheets被高速缓存" /大量存储,并且刷新"这将是一个巨大的痛苦(或者不可能)。它们。

我的问题是,我是否可以设法让XSL与Saxon一起使用而不更改它们或预处理XML(修改SAX-Parser或StringReplacing等)?

目前我需要更改命名空间和函数调用,因为使用Xalan我在命名空间中使用了包,Saxon(似乎)想要这个类。

我可以完全控制" de.server.macro"的包和类结构和代码。

(用于测试)我使用的是Saxon-9B,但最后它将是Saxon-PE或Saxon-EE。

以下是我的最小化示例:

的Xalan

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:macro="xalan://de.server.macro">

<xsl:template match="/">
    <output>
        <xsl:text>Hello World!</xsl:text>
        <mymacro>
            <xsl:variable name="foo">5</xsl:variable>
            <xsl:value-of select="macro:data.setVar('testdata', $foo)"/>
            <xsl:value-of select="macro:data.getVar('testdata')"/>
        </mymacro>
    </output>
</xsl:template>

撒克逊

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:macro="de.server.macro.data">

<xsl:template match="/">
    <output>
        <xsl:text>Hello World!</xsl:text>
        <mymacro>
            <xsl:variable name="foo">5</xsl:variable>
            <xsl:value-of select="macro:setVar('testdata', $foo)"/>
            <xsl:value-of select="macro:getVar('testdata')"/>
        </mymacro>
    </output>
</xsl:template>

我了解http://xml.apache.org/xalan-j/extensions.htmlhttp://www.saxonica.com/documentation9.5/extensibility/functions/staticmethods.html以及其他一些关于xslt-extensions的消息来源(博客文章和书籍),但基于这些信息,它的答案似乎是&#34; no&#34;

但也许我会遗漏撒克逊人的某些或某些可能的映射机制来模拟正确的行为?

2 个答案:

答案 0 :(得分:2)

我认为有一些格式的子集可以在Xalan和Saxon中运行。如果你使用Xalan所谓的&#34;类格式&#34;命名空间,例如

xmlns:String="xalan://java.util.Hashtable"

那么你应该可以调用静态方法,例如

String:valueOf($x)

在任一产品中;在Saxon的情况下,您需要设置配置属性FeatureKeys.ALLOW_OLD_JAVA_URI_FORMAT

答案 1 :(得分:1)

我使用以下解决方案来解决我的(特定)问题: 我发布了两个变体,因为Saxon-B(v9.1.0.8)和Saxon-EE(v9.6.0.7)之间存在一些差异。

在所有其他现有的FunctionLibrarys之前添加我的特殊FunctionLibrary,以确保在未触发我的特殊情况时的默认处理:

撒克逊-B

net.sf.saxon.TransformerFactoryImpl saxonFactory = (net.sf.saxon.TransformerFactoryImpl)tFactory;
FunctionLibraryList fll = new FunctionLibraryList();
fll.addFunctionLibrary( new OldXalanFunctionLibrary() );
fll.addFunctionLibrary( saxonFactory.getConfiguration().getExtensionBinder("java") );
saxonFactory.getConfiguration().setExtensionBinder("java", fll);

撒克逊-EE

net.sf.saxon.TransformerFactoryImpl saxonFactory = (net.sf.saxon.TransformerFactoryImpl)tFactory;
FunctionLibraryList fll = new FunctionLibraryList(); 
fll.addFunctionLibrary( new OldXalanFunctionLibrary() );
ProfessionalConfiguration conf = (ProfessionalConfiguration)saxonFactory.getConfiguration();
fll.addFunctionLibrary( conf.getExtensionBinder("java") );
conf.setExtensionBinder("java", fll);

我的特殊处理功能库如下所示:

撒克逊-B

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import de.server.MacroClasses;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.StaticContext;
import net.sf.saxon.functions.ExtensionFunctionCall;
import net.sf.saxon.functions.FunctionLibrary;
import net.sf.saxon.om.StructuredQName;
import net.sf.saxon.trans.XPathException;


public class OldXalanFunctionLibrary implements FunctionLibrary {

    private static final long serialVersionUID = 2216303509238422532L;

    public OldXalanFunctionLibrary() {
    }

    @Override
    public boolean isAvailable(StructuredQName functionName, int arity) {
        String uri = functionName.getNamespaceURI();
        String local = functionName.getLocalName();
        if (uri.equals("xalan://de.server.macro") && (local.indexOf(".") > 0)) {
            Class c = MacroClasses.class;
            Method[] methods = c.getMethods();
            String searchName = local.substring(local.lastIndexOf(".")+1);  
            for (int i=0; i< methods.length; i++) {
                if (methods[i].getName().equals(searchName)) {
                    if (methods[i].getParameterTypes().length == arity) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public Expression bind(StructuredQName functionName, Expression[] staticArgs, StaticContext env) throws XPathException {
        String uri = functionName.getNamespaceURI();
        String local = functionName.getLocalName();

        if (uri.equals("xalan://de.server.macro") && (local.indexOf(".") > 0)) {
            Class c = MacroClasses.class;
            Method[] methods = c.getMethods();
            Method m = null;
            String searchName = local.substring(local.lastIndexOf(".")+1);  
            for (int i=0; i< methods.length; i++) {
                //String name = methods[i].getName();
                if (methods[i].getName().equals(searchName)) {
                    if (methods[i].getParameterTypes().length == staticArgs.length) {
                        m = methods[i];
                        break;
                    }
                }
            }

            AccessibleObject accessObj = (AccessibleObject)m;

            ExtensionFunctionCall fn;
            try {
                fn = (ExtensionFunctionCall)(c.newInstance());
            } catch (InstantiationException e) {
                throw new IllegalArgumentException(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException(e.getMessage());
            }

            fn.init(functionName, c, accessObj, env.getConfiguration());
            fn.setArguments(staticArgs);
            return fn;
        }
        return null;


    }

    @Override
    public FunctionLibrary copy() {
        OldXalanFunctionLibrary newLibrary = new OldXalanFunctionLibrary();
        return newLibrary;
    }

}

撒克逊-EE

import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import com.saxonica.expr.JavaExtensionFunctionCall;
import de.server.MacroClasses;
import net.sf.saxon.expr.Container;
import net.sf.saxon.expr.Expression;
import net.sf.saxon.expr.StaticContext;
import net.sf.saxon.functions.FunctionLibrary;
import net.sf.saxon.om.FunctionItem;
import net.sf.saxon.trans.SymbolicName;
import net.sf.saxon.trans.XPathException;

public class OldXalanFunctionLibrary implements FunctionLibrary {

    public OldXalanFunctionLibrary() {
    }

    @Override
    public boolean isAvailable(SymbolicName symName) {
        String uri = symName.getComponentName().getNamespaceBinding().getURI();
        String local = symName.getComponentName().getStructuredQName().getLocalPart();
        if (uri.equals("xalan://de.server.macro") && (local.indexOf(".") > 0)) {
            Class<MacroClasses> c = MacroClasses.class;
            Method[] methods = c.getMethods();
            String searchName = local.substring(local.lastIndexOf(".")+1);  
            for (int i=0; i< methods.length; i++) {
                if (methods[i].getName().equals(searchName)) {
                    if (methods[i].getParameterTypes().length == symName.getArity()) {
                        return true;
                    }
                }
            }
        }
        return false;
    }

    @Override
    public Expression bind(SymbolicName symName, Expression[] staticArgs, StaticContext context, Container cont) throws XPathException {
        String uri = symName.getComponentName().getNamespaceBinding().getURI();
        String local = symName.getComponentName().getStructuredQName().getLocalPart();

        if (uri.equals("xalan://de.server.macro") && (local.indexOf(".") > 0)) {
            Class<MacroClasses> c = MacroClasses.class;
            Method[] methods = c.getMethods();
            Method m = null;
            String searchName = local.substring(local.lastIndexOf(".")+1);  
            for (int i=0; i< methods.length; i++) {
                //String name = methods[i].getName();
                if (methods[i].getName().equals(searchName)) {
                    if (methods[i].getParameterTypes().length == symName.getArity()) {
                        m = methods[i];
                        break;
                    }
                }
            }

            AccessibleObject accessObj = (AccessibleObject)m;

            JavaExtensionFunctionCall fn;
            try {
                fn = (JavaExtensionFunctionCall)(c.newInstance());
            } catch (InstantiationException e) {
                throw new IllegalArgumentException(e.getMessage());
            } catch (IllegalAccessException e) {
                throw new IllegalArgumentException(e.getMessage());
            }

            fn.init(symName.getComponentName().getStructuredQName(), c, accessObj);
            fn.setArguments(staticArgs);
            return fn;
        }
        return null;
    }

    @Override
    public FunctionLibrary copy() {
        OldXalanFunctionLibrary newLibrary = new OldXalanFunctionLibrary();
        return newLibrary;
    }

    @Override
    public FunctionItem getFunctionItem(SymbolicName symName, StaticContext context, Container cont) throws XPathException {
        return null;
    }

}

My Wrapper-Class用于调用我的特定方法:

撒克逊-B

package de.server;

import net.sf.saxon.functions.ExtensionFunctionCall;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import de.macro.Format;
import de.macro.TrimLine;
import de.macro.data;
import de.macro.exception.JavaStaticTransformationException;

public class MacroClasses extends ExtensionFunctionCall {

    // data
    public static Document getXmlDoc() {
        return data.getXmlDoc();
    }

    public static void setVar(String name, String value) {
        data.setVar(name, value);
    }

    public static void setVar(String name, String value, String context) {
        data.setVar(name, value, context);
    }

    public static Node getVar(String name) {
        return data.getVar(name);
    }

    public static Node getVar(String name, String context) {
        return data.getVar(name, context);
    }

    public static void flush() {
        data.flush();
    }

    public static String countContextItems() {
        return data.countContextItems();
    }

    public static String getContextsAsString() {
        return data.getContextsAsString();
    }

    public static String countAllItems() {
        return data.countAllItems();
    }

    public static void setOutdatedTime(long aInterval) {
        data.setOutdatedTime(aInterval);
    }

    // Format
     public static String format(String aVarName, String aDataType, String aInputString, String aFormatType, String aPrecision) throws Exception {
         return Format.format(aVarName, aDataType, aInputString, aFormatType, aPrecision);
     }

     public static String format(String aVarName, String aDataType, String aInputString) throws Exception {
         return Format.format(aVarName, aDataType, aInputString);
     }

     public static String format(String aVarName, String aDataType, String aInputString, String aSwitchSign,
             String aFormatType, String aPrecision) throws Exception {
         return Format.format(aVarName, aDataType, aInputString, aSwitchSign, aFormatType, aPrecision);
     }

     public static String convertToEnglish(String aInputString) {
         return Format.convertToEnglish(aInputString);
     }

     public static String convertLastMonthsLast(String aInputString) {
         return Format.convertLastMonthsLast(aInputString);
     }

     public static String getCountSelected(String aVarName, String aValue)
             throws JavaStaticTransformationException {
         return Format.getCountSelected(aVarName, aValue);
     }

     public static String fill(String aVarName, String aInputString, String aFillChar, String aLength,
             String aDirection) throws JavaStaticTransformationException {
         return Format.fill(aVarName, aInputString, aFillChar, aLength, aDirection);
     }

     // TrimLine
     public static String process(String aInput, int aMaxLineLength) throws Exception {
         return TrimLine.process(aInput, aMaxLineLength);
     }
}

撒克逊-EE

package de.server;

import org.w3c.dom.Document;
import org.w3c.dom.Node;

import com.saxonica.expr.JavaExtensionFunctionCall;

import de.macro.Format;
import de.macro.TrimLine;
import de.macro.data;
import de.macro.exception.JavaStaticTransformationException;

public class MacroClasses extends JavaExtensionFunctionCall {

    // data
    public static Document getXmlDoc() {
        return data.getXmlDoc();
    }

    private static String convert4setVar(Object value) throws Exception {
        String setValue;

        if (value instanceof String)
        {
            setValue = (String)value;
        }
        else if (value instanceof net.sf.saxon.value.TextFragmentValue)
        {
            net.sf.saxon.value.TextFragmentValue newTextValue = (net.sf.saxon.value.TextFragmentValue)value;
            setValue = newTextValue.getStringValue();
        }
        else if (value instanceof Integer)
        {
            setValue = Integer.toString((Integer)value);
        }
        else if (value instanceof Double)
        {
            setValue = Double.toString((Double)value);
        }
        else if (value instanceof java.math.BigInteger)
        {
            java.math.BigInteger newIntegerValue = (java.math.BigInteger)value;
            setValue = newIntegerValue.toString();
        }
        else
        {
            throw new Exception("Type for data.setVar not implemented: " + value.getClass().getName());
        }
        return setValue;
    }

    public static void setVar(String name, Object value) throws Exception {
        data.setVar(name, convert4setVar(value));
    }

    public static void setVar(String name, String value, String context) throws Exception {
        data.setVar(name, convert4setVar(value), context);
    }

    public static Node getVar(String name) {
        return data.getVar(name);
    }

    public static Node getVar(String name, String context) {
        return data.getVar(name, context);
    }

    public static void flush() {
        data.flush();
    }

    public static String countContextItems() {
        return data.countContextItems();
    }

    public static String getContextsAsString() {
        return data.getContextsAsString();
    }

    public static String countAllItems() {
        return data.countAllItems();
    }

    public static void setOutdatedTime(long aInterval) {
        data.setOutdatedTime(aInterval);
    }

    // Format
     public static String format(String aVarName, String aDataType, String aInputString, String aFormatType, String aPrecision) throws Exception {
         return Format.format(aVarName, aDataType, aInputString, aFormatType, aPrecision);
     }

     public static String format(String aVarName, String aDataType, String aInputString) throws Exception {
         return Format.format(aVarName, aDataType, aInputString);
     }

     public static String format(String aVarName, String aDataType, String aInputString, String aSwitchSign,
             String aFormatType, String aPrecision) throws Exception {
         return Format.format(aVarName, aDataType, aInputString, aSwitchSign, aFormatType, aPrecision);
     }

     public static String convertToEnglish(String aInputString) {
         return Format.convertToEnglish(aInputString);
     }

     public static String convertLastMonthsLast(String aInputString) {
         return Format.convertLastMonthsLast(aInputString);
     }

     public static String getCountSelected(String aVarName, String aValue)
             throws JavaStaticTransformationException {
         return Format.getCountSelected(aVarName, aValue);
     }

     public static String fill(String aVarName, String aInputString, String aFillChar, String aLength,
             String aDirection) throws JavaStaticTransformationException {
         return Format.fill(aVarName, aInputString, aFillChar, aLength, aDirection);
     }

     // TrimLine
     public static String process(String aInput, int aMaxLineLength) throws Exception {
         return TrimLine.process(aInput, aMaxLineLength);
     }

}

现在我不需要修改任何(缓存/保存的)XSLT-Stylesheets,它只捕获特定的案例。所以我可以使用正常的Saxon-Style调用来生成新生成的Stylesheets,但我可以向下兼容。我希望我没有明显的性能损失(我的测试直到现在才证实这一估计)。