Search code examples
xsltsaxon

How to write a Saxon ExtensionFunctionDefinition returning a Node


Using Saxon HE 12, I managed to create a Saxon ExtensionFunctionDefinition returning a SequenceType.SINGLE_STRING and using it in a XSLT transformation ; but I have a hard time making one returning a Node, let's say a SequenceType.SINGLE_NODE for this exemple.

Relevant part of the ExtensionFunctionDefinition extending class is :


    @Override
    public ExtensionFunctionCall makeCallExpression() {
        return new ExtensionFunctionCall() {
            @Override
            public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
                Processor processor  = new Processor();
                net.sf.saxon.s9api.DocumentBuilder documentBuilder = processor.newDocumentBuilder();
                Document document = buildSomeDocument();
                XdmNode wrapped = documentBuilder.wrap(document.getFirstChild());
                return wrapped.getUnderlyingNode();
            }
        };
    }

where buildSomeDocument returns a org.w3c.dom.Document

The code compiles fine but execution throws an exception :

 SXXP0004  Node returned by extension function was created with an incompatible Configuration

How to pass the configuration ?

What would be the simplest way to return a (wrapped) org.w3c.dom Element or Node ?

-- edit Creating the Transformer starting from new Processor() instead of TransformerFactory, HelloNode beeing the ExtensionFunctionDefinition

The code for the transformation is :


        Processor processor = new Processor();
        processor.registerExtensionFunction(new HelloNode());

        Path xslPath = Path.of("./some/path/to/testjavafunction.xsl");
        XsltCompiler xsltCompiler = processor.newXsltCompiler();
        XsltExecutable xsltExecutable = xsltCompiler.compile(xslPath.toFile());
        XsltTransformer xsltTransformer = xsltExecutable.load();

        // xml source
        Path xmlPath = Path.of("./some/path/to/testjavafunction.xml");
        InputStream inputStream = new FileInputStream(xmlPath.toFile());
        InputSource inputSource = new InputSource(inputStream);
        Source source = new SAXSource(inputSource);
        xsltTransformer.setSource(source);

        // destination
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Serializer serializerDestination = processor.newSerializer(byteArrayOutputStream);
        xsltTransformer.setDestination(serializerDestination);

        // transform
        xsltTransformer.transform();

        // dbg print
        System.out.println(byteArrayOutputStream.toString("UTF-8"));

the call method is now updated to :

   @Override
   public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
         Configuration.ApiProvider processor0 = context.getConfiguration().getProcessor();
         Processor processor = (Processor)processor0;
         net.sf.saxon.s9api.DocumentBuilder documentBuilder = processor.newDocumentBuilder();
         Document document = makeSomeDOM();

         XdmNode wrapped = documentBuilder.wrap(document.getFirstChild());
         return wrapped.getUnderlyingNode();
   }      


makeSomeDommethod is :


private static Document makeSomeDom() {
 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
 DocumentBuilder db = dbf.newDocumentBuilder();
 Document document = db.newDocument();
 Element h1 = document.createElement("h1");
 h1.setTextContent("my-content");
 document.appendChild(h1);
 return document;
}



Solution

  • Spelling out my suggestion from the comment, the code is e.g.

    public ExtensionFunctionCall makeCallExpression() {
          return new ExtensionFunctionCall() {
              @Override
              public Sequence call(XPathContext context, Sequence[] arguments) throws XPathException {
                  Processor processor = (Processor)context.getConfiguration().getProcessor();
                  DocumentBuilder documentBuilder = processor.newDocumentBuilder();
                  Document document = null;
                  try {
                      document = buildSomeDocument();
                  } catch (ParserConfigurationException e) {
                      throw new RuntimeException(e);
                  }
                  XdmNode wrapped = documentBuilder.wrap(document);
                  return wrapped.getUnderlyingNode();
              }
          };
    }
    

    The code to set up the processor and register the extension function:

    Processor processor = new Processor();
    
    processor.getUnderlyingConfiguration().setProcessor(processor);
    
    processor.registerExtensionFunction(new MyExtensionFunctionDefinition());
    

    All visible in https://github.com/martin-honnen/SaxonHE12ExtensionFunctionDomNodeExample1.