passing xml nodes/documents/fragments as parameters to xslt
Asked Answered
A

5

8

I tried to pass a w3c.dom.Document, Element and NodeList as parameters to a xslt transform.

I want to be able to process it within the xslt:

<xsl:param name="links" />
<xsl:template match="/">
    <record>
        <xsl:for-each select="$links/*">
            <test />
        </xsl:for-each>
    </record>
</xsl:template>

I pass the parameter as:

        Document params = createLinksParams(links);
        transformer.setParameter("links", params);

I get this exception:

'Invalid conversion from 'com.sun.org.apache.xerces.internal.dom.DocumentImpl' to 'node-set'.'

I tried also exslt:node-set(), xalan:nodeset() etc, but it doesn't work.

It seems that internally xalan excepts his own implementation of the Node.

How can I do something similar without incurring in this problem?

I cannot use document($param) because I construct the doc on the fly.

Agretha answered 24/9, 2010 at 16:24 Comment(0)
B
4

(Posting a new answer, as the previous one did not solve the issue and this new one is radically different from the previous)

Seems to be a known issue with XALAN compiling processor ( XALANJ-2057, How can I pass a node as parameter to translets for XSLTC Processor).

So, what are the alternatives?

  1. mess around with URIs as outlined in a response to How can I pass a node as parameter to translets for XSLTC Processor post
  2. Instead of XALAN compiling processor (XSLTC), use XALAN interpretive processor. Or any other XSLT processor that supports such behavior.
  3. Use DTMAxisIterator instead, also outlined in a response to How can I pass a node as parameter to translets for XSLTC Processor post - not sure if it will work, though.
  4. Create a new DOM tree, combining your "parameter" DOM and the original XSLT input document
Blueing answered 25/9, 2010 at 10:48 Comment(3)
Solution 3 doesn't seem to work when the node to be passed as param comes from a DOM document which is not the document being transformed.Agretha
Some links are deadBolme
Fixed the linksBlueing
C
4

I found a solution (here: XSLT Processing with Java : passing xml content in parameter) which may work for your case as well:

String urls = "<urls><url id='google'>https://www.google.com</url>...";
trans.setParameter("lookupdoc", new StreamSource(new StringReader(urls)));

instead of creating a uriresolver from a string, just create a stream source from a string reader and pass it to the stylesheet.

After that, I was able to access the doc normally as XML:

<xsl:param name="lookupdoc"><urls/></xsl:param> 
... 
<xsl:variable name="googleurl" select="$lookupdoc/@id='google"/>

Did not test with xalan, but maybe the answer will help others who stumble onto this question :)

Cos answered 27/12, 2012 at 16:31 Comment(0)
T
1

Here's a working example with the URIResolver Gambit, #1 in the list of solutions:

import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.URIResolver;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
import java.io.StringWriter;

public class XSLTest {
    public static void main(String[] args) {

        class MyResolver implements URIResolver {
            String _xml;
            MyResolver(String xml) { _xml = xml; }
            @Override
            public Source resolve(String href, String base) throws TransformerException {
                return new StreamSource(new StringReader(_xml));
            }
        }

        String lookup =
            "<?xml version='1.0' encoding='utf-8'?>\n" +
            "<urls>\n" +
            "    <url id='google'>https://www.google.com</url>\n" +
            "    <url id='yahoo'>https://www.yahoo.com</url>\n" +
            "    <url id='apple'>https://www.apple.com</url>\n" +
            "</urls>";

        String main =
            "<?xml version='1.0' encoding='utf-8'?>" +
            "<list>"+
            "   <link ref='yahoo'>Yahoo</link>"+
            "   <link ref='google'>Google</link>"+
            "</list>";

        String xsl =
            "<?xml version='1.0' encoding='UTF-8'?>\n" +
            "<xsl:stylesheet version='1.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>\n" +
            "    <xsl:param name='lookup-doc' />\n" +
            "    <xsl:variable name='lookup' select='document($lookup-doc)'/>\n" +
            "    <xsl:template match='/'>\n" +
            "        <xsl:for-each select='//link'>\n" +
            "            <xsl:variable name='ref' select='@ref'/>\n" +
            "            <xsl:element name='a'>\n" +
            "                <xsl:attribute name='href'>\n" +
            "                    <xsl:value-of select='$lookup//url[@id=$ref]'/>\n" +
            "                </xsl:attribute>\n" +
            "                <xsl:value-of select='text()'/>\n" +
            "            </xsl:element>\n" +
            "        </xsl:for-each>\n" +
            "    </xsl:template>\n" +
            "</xsl:stylesheet>";

        try {

            // xsl doc
            Source xsltSource = new StreamSource(new StringReader(xsl));

            TransformerFactory transFact = TransformerFactory.newInstance();
            Transformer trans = transFact.newTransformer(xsltSource);

            // main doc
            Source mainSource = new StreamSource(new StringReader(main));

            // lookup doc - stage it in the URI resolver
            trans.setURIResolver(new MyResolver(lookup));
            // dummy URL, you could use different values here to 
            // support multiple document parameters
            trans.setParameter("lookup-doc", "xml://lookup");

            StringWriter out = new StringWriter();
            trans.transform(mainSource, new StreamResult(out));

            System.out.println(out.toString());

        } catch (TransformerException e) {
            System.err.println("It's the wrong trousers Gromit, and they've gone wrong!");
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }

    }
}

I also had a working version where I put the xml source in the URI like

xml://<urls><url><url id='google'>https://www.google.com</url>...

but I figured that might run into length limits somewhere.

Tenor answered 8/11, 2012 at 1:55 Comment(0)
B
0

If you look at the Document JavaDoc, you can see that it extends Node interface, but not NodeList. Not sure if it will work, but you could try to pass in params.getChildNodes() instead of params.

Blueing answered 24/9, 2010 at 17:32 Comment(7)
Maybe it is related to this bug? bugs.sun.com/bugdatabase/view_bug.do?bug_id=5059947Blueing
Anyway, I would suggest to take the stacktrace of that error and track down in Xalan source code the specific circumstance when this exception is thrown.Blueing
Yes, before posting here I checked the sources:Agretha
java.lang.RuntimeException: Invalid conversion from 'com.sun.org.apache.xerces.internal.dom.DocumentImpl' to 'node-set'. at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.runTimeError(BasisLibrary.java:1523) at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.runTimeError(BasisLibrary.java:1531) at com.sun.org.apache.xalan.internal.xsltc.runtime.BasisLibrary.referenceToNodeSet(BasisLibrary.java:999)Agretha
method "referenceToNodeSet" accepts only "com.sun.org.apache.xalan.internal.xsltc.runtime.Node" instances, which are wrappers to dom nodes. There is a helper used to create those wrappers, in BasisLibrary, which requires a "Translet" instance as a mandatory parameter. The translet instance has to be taken from a protected member of the TransformerImpl instance. Unfortunately the jar is sealed and I get a access exception trying to access that protected getter.Agretha
I'm also interested in alternative approaches to my problem. For now I will quickly hack my solution by wrapping the input document together with my parameter class "<package><params>...</params><originaldoc</package>". But I don't really like this kind of hacks. Other ideas would be to create a custom url protocol and use 'select="document('mydummyprotocol:somekey')' and hook 'somekey' to my generated xml. Also this sounds too hackish.Agretha
Seems to be a known issue with XALAN compiling processor ( issues.apache.org/jira/browse/XALANJ-2057, old.nabble.com/…).Blueing
I
0

It's an annoyance, one way or the other. In the end, I've always found it easiest and most compatible with other XSLT processors to serialize the XML fragment to a temp file, pass that file's URI to XSLT as a string parameter, and from there load the URI via the XPath document() function.

Imprecation answered 13/8, 2019 at 15:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.