passing xml nodes/documents/fragments as parameters to xslt

我的未来我决定 提交于 2019-12-21 11:35:15

问题


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.


回答1:


(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



回答2:


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




回答3:


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.




回答4:


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.




回答5:


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.



来源:https://stackoverflow.com/questions/3789044/passing-xml-nodes-documents-fragments-as-parameters-to-xslt

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!