0

I'm playing with XSLT and writing the result to the multiple outputs in Java as these links describe: link1, link2.

When it comes to writing to the files everything works perfectly, but now I'd like to store the output XMLs as either Document object or some kind of InputStream/String - anything but file on a drive. Writing the documents to the files using Transformer and than reading them to the objests(Document, String etc.) is not an option.

My question is: what should I change in my xslt map and java code to get the output as an objects, not the files on a drive. (the only solution that I came up with is to use two XSLT on this one XML but it's not the elegant way)

My Java codes are similar to the answers in the links above. I'm using XSLT 2.0 and transform using :

transformer.transform(xmlText, new DOMResult());

I also added

  <xsl:result-document href="{concat($jobName, '_assets.xml')}">
        <xsl:apply-templates select="GetMetadata/Metadata" mode="assets"/>
    </xsl:result-document>
    <xsl:result-document href="{concat($jobName, '_flows.xml')}">
        <xsl:apply-templates select="GetMetadata/Metadata" mode="flows"/>
    </xsl:result-document>

to my xsl map [writing to the files works fine]

LATER

Correct implementation: (thank you Michael)

public void multipleXMLExtractor(Document inputDom, String xsltPath) throws TransformerException, UnsupportedEncodingException {

Source xmlInput = new DOMSource(inputDom);
Source xsltMap = new StreamSource(new File(xsltPath));
final Map<String, StreamResult> resultsMap = new HashMap<String, StreamResult>();
resultsMap.put("output1", new StreamResult(new ByteArrayOutputStream()));
resultsMap.put("output2", new StreamResult(new ByteArrayOutputStream()));

TransformerFactory tFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", XMLviaXSLTransformer.class.getClassLoader());
Transformer transformer = tFactory.newTransformer(xsltMap);
((net.sf.saxon.jaxp.TransformerImpl) transformer).getUnderlyingController().setOutputURIResolver(new OutputURIResolver() {
    @Override
    public OutputURIResolver newInstance() {
        return this;
    }

    @Override
    public Result resolve(String s, String s1) throws TransformerException {
        return resultsMap.get(s);
    }

    @Override
    public void close(Result result) throws TransformerException {
    }
});

StreamResult standardResult = new StreamResult(new ByteArrayOutputStream());
transformer.transform(xmlInput, standardResult);


for (Map.Entry<String, StreamResult> stringStreamResultEntry : resultsMap.entrySet()) {
    System.out.println(stringStreamResultEntry.getKey() + ": " + ((ByteArrayOutputStream) stringStreamResultEntry.getValue().getOutputStream()).toString("UTF-8"));
}

}

3
  • Please tell us exactly which version of which XSLT 2 processor you use. If it is Saxon 9 and you use the JAXP API for running XSLT 2 then you might want to switch the the Saxon 9 API to get access to the Configuration (although I think you can do that through JAXP with Saxon specific code as well) and then set saxonica.com/html/documentation/javadoc/net/sf/saxon/… to handle the result documents as needed. Commented Dec 5, 2017 at 11:43
  • "net.sf.saxon.TransformerFactoryImpl" I'm using processor : Saxon - HE 9.8.0-3 and in import javax.xml... I still can't figure out how to implement outputting not to the file, but to some stream Commented Dec 5, 2017 at 12:45
  • @MartinHonnen because of thread safety issues it's probably safer to set the OutputURIResolver at the Controller level rather than on the Configuration. Commented Dec 5, 2017 at 12:56

1 Answer 1

3

You need to define an OutputURIResolver - this is a Saxon-specific interface that is called to determine the destination for the output of xsl:result-document. It is given a URI as input, and returns a JAXP Result object to which the result document will be written.

https://www.saxonica.com/documentation/index.html#!javadoc/net.sf.saxon.lib/OutputURIResolver

If you're using the JAXP API to control the transformation, you will have to cast the Transformer object to net.sf.saxon.jaxp.TransformerImpl, and then call getUnderlyingController().setOutputURIResolver(), supplying your OutputURIResolver as the argument.

Sign up to request clarification or add additional context in comments.

6 Comments

Michael - I've problem with OutputURIResolver implementation, especially with the resolve method. The examples of the implementations that I found, save file to the base directory under the name - href that is href from xsl:result-document. I couldn't find the example that describes how to cope with the second output. I guess both are in Result tree, am I right? I think that I don't call the resolve method, saxon somehow does. So I just need to properly Implement OutputURIResolver and get both XML outputs into Document obj. from Result obj. Would you mind helping me with that problem?
Yes, Saxon calls the resolve() method. Your code looks good. Are you sure the names "output1" and "output2" match the href values in your xsl:result-document calls? Add some diagnostic output or debugger breakpoints to see what calls Saxon is making on your resolve() method. It would be more robust to create entries in the map when resolve() is called, rather than trying to pre-allocate them.
Yes the names match(I'm running everything on basic xml and xsl). Values for keys "output1" and "output2" in my resultsMap are empty. Something is wrong probably with the resolve(). Maybe close() shouldn't be empty?
So what did debugging tell you? Does resolve() get called, and with what arguments?
My resolve() didn't get called, only resolve() from StandardOutputResolver class so the transform saves the output files in a base location. I understand that using setOutputURIResolver(new OutputURIResolver() {...} overloads the methods from StandardOutputResolver. That is why in my resolve() I can do whatever I want with the output - in my case put them into map, but it doesn't seem to work. Maybe I did something wrong with the map or newInstance()?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.