Bug #6166
closedPublic fields in a Java instance object cannot be accessed from XSLT
100%
Description
The documentation on reflexive extension functions to XSLT/XPath/XQuery in e.g. https://www.saxonica.com/html/documentation12/extensibility/extension-functions-J/reflexive-functions/ always talks about "method, or field of the Java class" but while I get access to an instance method working I don't get a stylesheet compiled which I hope to access a public field.
Is there any sample showing that?
Why does the following code fail?
package org.example;
import net.sf.saxon.s9api.*;
import javax.xml.transform.stream.StreamSource;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void main(String[] args) throws SaxonApiException {
Processor processor = new Processor(true);
XsltCompiler xsltCompiler = processor.newXsltCompiler();
XsltExecutable xsltExecutable = null;
try {
xsltExecutable = xsltCompiler.compile(new StreamSource("processPerson2Test1.xsl"));
}
catch (SaxonApiException e) {
System.out.println(e.getMessage());
e.printStackTrace();
return;
}
Xslt30Transformer xslt30Transformer = xsltExecutable.load30();
Map<QName, XdmValue> parameters = new HashMap<>();
//parameters.put(new QName("person1"), new XdmExternalObject(new Person1("John")));
parameters.put(new QName("person2"), new XdmExternalObject(new Person2("John")));
xslt30Transformer.setStylesheetParameters(parameters);
xslt30Transformer.callTemplate(null, processor.newSerializer(System.out));
}
}
package org.example;
public class Person2 {
public Person2(String name) {
this.name = name;
}
public String name;
}
Tested with Saxon 12.3 EE and the XSLT
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jt="http://saxon.sf.net/java-type"
xmlns:Person2="java:org.example.Person2"
exclude-result-prefixes="#all">
<xsl:param name="person2" as="jt:org.example.Person2" required="true"/>
<xsl:output method="adaptive" indent="yes"/>
<xsl:template name="xsl:initial-template">
<xsl:sequence
select="Person2:name($person2)"/>
</xsl:template>
</xsl:stylesheet>
XSLT Compilation fails with a NullPointerException:
Exception in thread "main" java.lang.NullPointerException
at com.saxonica.expr.JavaExtensionFunctionCall.typeCheck(JavaExtensionFunctionCall.java:205)
at net.sf.saxon.style.StyleElement.typeCheck(StyleElement.java:1560)
at net.sf.saxon.style.XSLSequence.validate(XSLSequence.java:94)
at net.sf.saxon.style.StyleElement.validateSubtree(StyleElement.java:1740)
at net.sf.saxon.style.StyleElement.validateChildren(StyleElement.java:1773)
at net.sf.saxon.style.StyleElement.validateSubtree(StyleElement.java:1744)
at net.sf.saxon.style.XSLTemplate.validateSubtree(XSLTemplate.java:610)
at net.sf.saxon.style.PrincipalStylesheetModule.preprocess(PrincipalStylesheetModule.java:398)
at net.sf.saxon.style.Compilation.compilePackage(Compilation.java:290)
at net.sf.saxon.style.StylesheetModule.loadStylesheet(StylesheetModule.java:254)
at net.sf.saxon.style.Compilation.compileSingletonPackage(Compilation.java:113)
at net.sf.saxon.s9api.XsltCompiler.compile(XsltCompiler.java:971)
at org.example.Main.main(Main.java:18)
Updated by Martin Honnen over 1 year ago
If I set the configuration feature processor.setConfigurationProperty(Feature.TRACE_EXTERNAL_FUNCTIONS, true);
, the output suggests the field is considered a match:
Looking for function Q{java:org.example.Person2}name#1
Trying net.sf.saxon.functions.registry.XSLT30FunctionSet
Trying net.sf.saxon.style.StylesheetFunctionLibrary
Trying net.sf.saxon.functions.FunctionLibraryList
Looking for function Q{java:org.example.Person2}name#1
Trying com.saxonica.ee.extfn.VendorFunctionSetEE
Trying net.sf.saxon.functions.MathFunctionSet
Trying net.sf.saxon.ma.map.MapFunctionSet
Trying net.sf.saxon.ma.arrays.ArrayFunctionSet
Trying com.saxonica.functions.extfn.EXPathBinaryFunctionSet
Trying net.sf.saxon.functions.registry.ExsltCommonFunctionSet
Trying com.saxonica.functions.extfn.EXPathFileFunctionSet
Trying net.sf.saxon.functions.registry.OnDemandFunctionSet
Function Q{java:org.example.Person2}name not found!
Trying net.sf.saxon.functions.registry.ConstructorFunctionLibrary
Trying net.sf.saxon.query.XQueryFunctionLibrary
Trying net.sf.saxon.functions.IntegratedFunctionLibrary
Trying com.saxonica.config.JavaExtensionLibrary
Loading org.example.Person2
Looking for method name in namespace java:org.example.Person2
Number of actual arguments = 1
Looking in Java class class org.example.Person2
Trying method wait: name does not match
Trying method wait: name does not match
Trying method wait: name does not match
Trying method equals: name does not match
Trying method toString: name does not match
Trying method hashCode: name does not match
Trying method getClass: name does not match
Trying method notify: name does not match
Trying method notifyAll: name does not match
Trying field name: name matches
Field is not static
Found a candidate field:
public java.lang.String org.example.Person2.name
So why the NullPointerException on compilation?
All done with Java 8 on my side.
Updated by Michael Kay over 1 year ago
I'm pretty sure I did this successfully quite recently but I'll take a look.
Updated by Martin Honnen over 1 year ago
I stepped through what the IDE decompiles from com.saxonica.expr.JavaExtensionFunctionCall.typeCheck(JavaExtensionFunctionCall.java, it seems there is a variable initialized as int firstArg = 0;
and for an instance method then the code with e.g.
if (this.theMethod instanceof Method) {
...
boolean isStatic = Modifier.isStatic(((Method)this.theMethod).getModifiers());
firstArg = isStatic ? 0 : 1;
...
}
sets that firstArg
to 1 for instance methods but there is no such change of firstArg
done for e.g. this.theMethod instanceof Field
, subsequently the code seemingly to handle external objects with e.g.
if (firstArg != 0) {
JavaExternalObjectType result;
synchronized(config) {
result = JavaExternalObjectType.of(this.theClass);
}
...
is never stepped into for fields although it appears it has the right code to handle the field of an external Java object too.
Updated by Michael Kay over 1 year ago
- Tracker changed from Support to Bug
- Subject changed from (How) Can public fields in a Java instance object be accessed from XSLT? to Public fields in a Java instance object cannot be accessed from XSLT
- Status changed from New to Resolved
- Applies to branch 11, trunk added
- Fix Committed on Branch 11, 12, trunk added
Yes, it appears that the type-checking for invocations of Java instance-level public fields always fails. The code clearly worked at some time in the distant past and is easily fixed.
I've added unit tests and we'll fix it on the 11 and 12 branches.
Updated by Debbie Lockett over 1 year ago
- % Done changed from 0 to 50
- Fixed in Maintenance Release 11.6 added
Bug fix applied in the Saxon 11.6 maintenance release.
Updated by O'Neil Delpratt about 1 year ago
- Status changed from Resolved to Closed
- % Done changed from 50 to 100
- Fixed in Maintenance Release 12.4 added
Bug fix applied in the Saxon 12.4 Maintenance release
Please register to edit this issue