Project

Profile

Help

How to find output method of primary result document if xsl:output doesn't declare it and Saxon infers it from the root element of the result?

Added by Martin Honnen almost 5 years ago

Using Saxon 9.9.1.2 HE, I am trying to set up my own Serializer subclass to capture the primary as well as secondary result documents as strings and to capture serialization/output properties like the output method.

I am now able to capture the output method in all but one case: if the primary output document has no method defined through an xsl:output Saxon correctly (as I can tell when looking the serialized result) infers the method based on the root element of the primary result document but somehow the method used doesn't show up in the properties my subclass reads out in its override of the getReceiver method.

My subclass looks like this:

public class MySerializer extends Serializer {

    private String method;

    public String getMethod() {
        return method;
    }

    private String defaultMethod;

    public String getDefaultMethod() {
        return defaultMethod;
    }

    private Writer outputWriter;

    public Writer getOutputWriter() {
        return outputWriter;
    }

    public MySerializer(Processor processor) {
        super(processor);
    }

    public MySerializer(Processor processor, Writer outputWriter) {
        super(processor);
        this.outputWriter = outputWriter;
        setOutputWriter(outputWriter);
    }

    @Override
    public Receiver getReceiver(PipelineConfiguration pipe, SerializationProperties params) throws SaxonApiException {
        method = params.getProperties().getProperty("method");
        return super.getReceiver(pipe, params);
    }

    @Override
    public Properties getCombinedOutputProperties(Properties defaultOutputProperties) {
        defaultMethod = defaultOutputProperties.getProperty("method");
        return super.getCombinedOutputProperties(defaultOutputProperties);
    }
  
}

I use it in a method like this:

    public void test(String xsltCode) throws SaxonApiException {
        Processor processor = new Processor(false);
        
        Xslt30Transformer transformer = processor.newXsltCompiler().compile(new StreamSource(new StringReader(xsltCode))).load30();
        
        transformer.setBaseOutputURI("urn:to-string");
        
        HashMap<String, MySerializer> serializers = new HashMap<>();
        
        transformer.setResultDocumentHandler(uri -> {
            MySerializer serializer = new MySerializer(processor, new StringWriter());
            serializers.put(uri.toASCIIString(), serializer);
            return serializer;
        });
        
        MySerializer primaryResultSerializer = new MySerializer(processor, new StringWriter());
        
        transformer.callTemplate(null, primaryResultSerializer); //processor.newSerializer(System.out));
        
        System.out.println("Primary result method '" + primaryResultSerializer.getMethod() + "'");
        System.out.println(primaryResultSerializer.getOutputWriter().toString());
        System.out.println();
       
        serializers.forEach((uri, serializer) -> {
            System.out.println("Result for URI " + uri + " with method '" + serializer.getMethod() + "'");
            System.out.println("defaultMethod:" + serializer.getDefaultMethod());
            System.out.println(serializer.getOutputWriter().toString());
            System.out.println();
        });
    }

If I pass in an XSLT like the following, where the primary result is not having any output method declared, Saxon serializes as html based on the root element, as I can see in the serialization result string, but the getMethod() for that MySerializer outputs null.

Any idea how to fix that, how to read out the used html output method for that result?

Interestingly enough for the secondary result set up with html-through-root-el-test1.html where also no output method is declared the used/inferred html method is available in the properties read out through params.getProperties().getProperty("method").

    private static final String xslt2 = "<xsl:stylesheet xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\"\n" +
"    xmlns:xs=\"http://www.w3.org/2001/XMLSchema\"\n" +
"    exclude-result-prefixes=\"xs\"\n" +
"    version=\"3.0\">\n" +
"    \n" +
"    <xsl:output name=\"json-test1\" method=\"json\" indent=\"yes\"/>\n" +
"    \n" +
"    <xsl:template name=\"xsl:initial-template\">\n" +
"        <html>\n" +
"            <head>\n" +
"                <title>Test</title>\n" +
"            </head>\n" +
"            <body>\n" +
"                <h1>Test</h1>\n" +
"                <p>Test paragraph with some line breaks: <br/>next line<br/>next line</p>\n" +
"            </body>\n" +
"        </html>\n" +
"        \n" +
"        <xsl:result-document href=\"html-through-root-el-test1.html\">\n" +
"        <html>\n" +
"            <head>\n" +
"                <title>Test</title>\n" +
"            </head>\n" +
"            <body>\n" +
"                <h1>Test</h1>\n" +
"                <p>Test paragraph with some line breaks: <br/>next line<br/>next line</p>\n" +
"            </body>\n" +
"        </html>\n" +
"        </xsl:result-document>\n" +
"        \n" +
"        <xsl:result-document href=\"json-through-result-doc-attr-1.json\" method=\"json\" indent=\"yes\">\n" +
"            <xsl:sequence select=\"map { 'test' : array { (1 to 5) ! ('test ' || .) } }\"/>\n" +
"        </xsl:result-document>\n" +
"\n" +
"        <xsl:result-document href=\"json-through-named-output-1.json\" format=\"json-test1\">\n" +
"            <xsl:sequence select=\"map { 'test' : array { (1 to 5) ! ('test ' || .) } }\"/>\n" +
"        </xsl:result-document>\n" +
"        \n" +
"    </xsl:template>\n" +
"    \n" +
"</xsl:stylesheet>";

Please register to reply