javaxmlxsltsaxon

How do I set the SystemId for an XSLT stylesheet in memory


(Inspired by an old question)

I have some stylesheets that use the anonymous document('') function to implement look-up tables.

<!-- example the lookup table data -->
<ref:data xmlns:ref="urn:inline-data">
    <phrasebook>
        <!-- English -->
        <lang code="en">
            <phrase code="USD_PREFIX">$ </phrase>
            <phrase code="GBP_PREFIX">£</phrase>
            <phrase code="EUR_PREFIX">€</phrase>
            <phrase code="JPY_PREFIX">¥</phrase>
        </lang>
        ...
    </phrasebook>
</ref:data>

<!-- the lookup table variable I can use elsewhere -->
<xsl:variable name="phrasebook" select="document('')/xsl:stylesheet/ref:data/phrasebook" xmlns:ref="urn:inline-data"/>

(the ref namespace is actually in the stylesheet definition, but included here for brevity)

The problem is that running such a stylesheet with a String StreamSource fails, but a File StreamSource works.

From this comment, I understand that it's because the SystemId is not set on the StreamSource for the Transformer to obtain. But to what should I set the SystemId for a String in memory, rather than a File?


Solution

  • You'll need to implement a custom URIResolver

    Source xmlSource = ...
    
    String xslt = "<xsl:stylesheet>...</xsl:stylesheet>";
    
    // use a supplier here to avoid duplication but ensure we get a new Reader each time
    Supplier<Source> xsltSourceSupplier = () -> {
       Source xsltSource = new InputSource(new StringReader(xslt));
       xsltSource.setSystemId("foo.xslt"); // always be sure to set systemId!
       return xsltSource;
    };
    
    TransformerFactory transformerFactory = TransformerFactory.newInstance();
    Transformer transformer = transformerFactory.newTransformer(xsltSourceSupplier.get());
    transformer.setURIResolver(new URIResolver() {
       public Source resolve(String href, String base) {
          System.out.println(String.format("resolve: href=%s, base=%s", href, base));
          if ("".equals(href)) {
             return xsltSourceSupplier.get();
          }
          throw new RuntimeException("TODO: Add support for resolving " + href);
       }
    }); 
    
    StreamResult result = new StreamResult("output.html");
    transformer.transform(xmlSource, result);