Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Resolving relative paths when loading XSLT files

I need to do an XSL transformation using Apache FOP and I had code like this:

//Setup FOP
Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF, out);
//Setup Transformer
Source xsltSrc = new StreamSource(new File(xslPath));
Transformer transformer = tFactory.newTransformer(xsltSrc);

//Make sure the XSL transformation's result is piped through to FOP
Result res = new SAXResult(fop.getDefaultHandler());
//Setup input
Source src = new StreamSource(new File(xmlPath));
//Start the transformation and rendering process
transformer.transform(src, res);

where xslPath is the path where my XSLT file is stored.

I have confirmed that it works when I have only one XSLT file, but in my project I have divided things into several XSLT files and joined them with the <xsl:import /> tag. With this configuration, I get a NullPointerException because it doesn't understand all the information stored in XSLT because it's distributed over different files.

I wonder if there's any way to load all these files in the Source xsltSrc variable so all the XSL information is available.

UPDATE

I've changed the code based on the answer given by Mads Hansen, but it still doesn't work. I have to include the XSLT slt files in the classpath, so I load the XSLT file with ClassLoader. I've checked that the URL has the correct path when executing url.toExternalForm(). This is my new piece of code:

ClassLoader cl = this.getClass().getClassLoader();
String systemID = "resources/xslt/myfile.xslt";
InputStream in = cl.getResourceAsStream(systemID);
URL url = cl.getResource(systemID);
Source source = new StreamSource(in);
source.setSystemId(url.toExternalForm());
transformer = tFactory.newTransformer(source);

It finds and loads myfile.xslt but it still doesn't resolve the relative paths to the other XSLT files.

What am I doing wrong?

like image 575
Javi Avatar asked Sep 13 '10 11:09

Javi


3 Answers

I just got it, a late answer(tested on FOP 1.0) ------

All you need is to set an uri resolver for your factory, as following works for me:

TransformerFactory transFact = TransformerFactory.newInstance();
StreamSource xsltSource = new StreamSource(xsl);

// XXX for 'xsl:import' to load other xsls from class path
transFact.setURIResolver(new ClasspathResourceURIResolver());
Templates cachedXSLT = transFact.newTemplates(xsltSource);
Transformer transformer = cachedXSLT.newTransformer();


class ClasspathResourceURIResolver implements URIResolver {
  @Override
  public Source resolve(String href, String base) throws TransformerException {
    return new StreamSource(XXX.getClassLoader().getResourceAsStream(href));
  }
}

and my importing xsl(so the 'imported.xsl' should be in the classpath):

<xsl:import href="META-INF/companybusinesscredit/imported.xsl"/>
like image 150
JunLei Avatar answered Nov 04 '22 15:11

JunLei


When you load an XSLT as a StreamSource and do not set a SystemID, the processor doesn't know "where" the XSLT is and cannot resolve relative paths.

http://www.onjava.com/pub/a/onjava/excerpt/java_xslt_ch5/index.html?page=5

By providing a system identifier as a parameter to the StreamSource, you are telling the XSLT processor where to look for commonFooter.xslt. Without this parameter, you may encounter an error when the processor cannot resolve this URI. The simple fix is to call the setSystemId( ) method as follows:

// construct a Source that reads from an InputStream
Source mySrc = new StreamSource(anInputStream);
// specify a system ID (a String) so the 
// Source can resolve relative URLs
// that are encountered in XSLT stylesheets
mySrc.setSystemId(aSystemId);
like image 44
Mads Hansen Avatar answered Nov 04 '22 15:11

Mads Hansen


I'm using Saxon 9.x and still had issues when I used document within the stylesheet. The stylesheet was correctly resolved but the xmls bundled along with the stylesheet in the jar file didn't load as expected even with setSystemId. It resulted in file not found exception. It was easier for me to custom code the resolver with the code below:

JarfileResolver jarfileResolver = new JarfileResolver();
transformer.setURIResolver(jarfileResolver);


public class JarfileResolver implements URIResolver
{
    public Source resolve(String fileName, String base) throws TransformerException
    {
        URL url = getClass().getClassLoader().getResource(fileName);
        StreamSource jarFileSS = new StreamSource();

        try
        {
            InputStream jarfileIS = url.openStream();
            jarFileSS.setInputStream(jarfileIS);
        }
        catch(IOException ioExp)
        {
            throw new TransformerException(ioExp);
        }
        return jarFileSS;
    }
}
like image 23
Anand Avatar answered Nov 04 '22 14:11

Anand