Resolving relative paths when loading XSLT files

31,343

Solution 1

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"/>

Solution 2

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);

Solution 3

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;
    }
}
Share:
31,343

Related videos on Youtube

Javi
Author by

Javi

Updated on July 23, 2020

Comments

  • Javi
    Javi almost 4 years

    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?

  • Javi
    Javi almost 14 years
    thanks for your answer, but it doesn't seem to solve my problem, though your answer makes sense. I've updated my question with my changes in the code.
  • Mads Hansen
    Mads Hansen almost 14 years
    You may need to obtain an absolute path for your SystemID. Unless you know where it will be resolving the relative path "from". Try converting that relative path into an absolute path with something like: String xsltSystemId = new File(systemID).toURL().toExternalForm( ); or String xsltSystemId = new File(relativePath).getAbsolutePath();
  • Javi
    Javi almost 14 years
    thanks, I had already though in that and I had checked that When I do url.toExternalForm() it gets an absolute path. But I don't know where's the problem now.
  • Mads Hansen
    Mads Hansen almost 14 years
    Is it possible that your import statements are not correct? (case sensitivity of filenames, incorrect filename, incorrect path, etc). Could there be an issue with file permissions? Try running your stylesheet through other means(in an IDE like Xselerator, oXygen, XML spy, or commandline invocation of Saxon, etc).
  • Javi
    Javi almost 14 years
    No my xslt files are correct. I can do the xsl conversion executing a command in the console without any problem. It's very weird. +1 for your answer because of your effort though I haven't solve the problem yet.
  • Daniel Bo
    Daniel Bo almost 9 years
    Thank you so much, this saved me hours.
  • james.garriss
    james.garriss over 8 years
    This solution worked perfectly for me: xsltStreamSource.setSystemId(xsltFile.toURI().toString());
  • M'λ'
    M'λ' over 4 years
    @james.garriss After several hours of crawling the web and reading wastefull comments not answering simply how to import relative files, suddendly, this "one-liner" of you solved my relative 'import' problem. I would give you 10000 upvote if I could ! :D
  • james.garriss
    james.garriss over 4 years
    Glad to help, @M'λ'.