Передача xml узлов / документов / фрагментов в качестве параметров в xslt
Я пытался передать w3c.dom.Document
, Element
а также NodeList
в качестве параметров для преобразования xslt.
Я хочу иметь возможность обрабатывать его в xslt:
<xsl:param name="links" />
<xsl:template match="/">
<record>
<xsl:for-each select="$links/*">
<test />
</xsl:for-each>
</record>
</xsl:template>
Я передаю параметр как:
Document params = createLinksParams(links);
transformer.setParameter("links", params);
Я получаю это исключение:
'Неправильное преобразование из'com.sun.org.apache.xerces.internal.dom.DocumentImpl'в' набор узлов '.'
Я пробовал также exslt:node-set()
, xalan:nodeset()
и т.д., но это не работает.
Кажется, что внутренне xalan исключает его собственную реализацию Node.
Как я могу сделать что-то подобное, не сталкиваясь с этой проблемой?
Я не могу использовать document($param)
потому что я создаю документ на лету.
4 ответа
(Размещение нового ответа, так как предыдущий не решил проблему, и этот новый радикально отличается от предыдущего)
Кажется, это известная проблема с процессором компиляции XALAN ( XALANJ-2057, Как я могу передать узел в качестве параметра для транслетов для XSLTC-процессора).
Итак, каковы альтернативы?
- возиться с URI, как описано в ответе на вопрос Как я могу передать узел в качестве параметра для транслетов для поста XSLTC Processor
- Вместо процессора компиляции XALAN (XSLTC) используйте интерпретирующий процессор XALAN. Или любой другой процессор XSLT, который поддерживает такое поведение.
- Вместо этого используйте DTMAxisIterator, также обрисованный в общих чертах в ответе на вопрос " Как передать узел в качестве параметра в транслеты для поста процессора XSLTC" - хотя и не уверен, что он будет работать.
- Создайте новое дерево DOM, комбинируя ваш DOM с "параметрами" и исходный входной документ XSLT.
Я нашел решение (здесь: XSLT Processing with Java: передача содержимого XML в параметре), которое также может работать для вашего случая:
String urls = "<urls><url id='google'>https://www.google.com</url>...";
trans.setParameter("lookupdoc", new StreamSource(new StringReader(urls)));
вместо создания Uriresolver из строки, просто создайте источник потока из программы чтения строк и передайте его в таблицу стилей.
После этого я смог получить доступ к документу как XML:
<xsl:param name="lookupdoc"><urls/></xsl:param>
...
<xsl:variable name="googleurl" select="$lookupdoc/@id='google"/>
Не тестировал с xalan, но, возможно, ответ поможет другим, кто наткнулся на этот вопрос:)
Вот рабочий пример с URIResolver Gambit, № 1 в списке решений:
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.
}
}
}
У меня также была рабочая версия, где я поместил исходный код XML в URI, как
xml://<urls><url><url id='google'>https://www.google.com</url>...
но я подумал, что это может где-то ограничивать длину.
Так или иначе, это раздражение. В конце концов, я всегда считал самым простым и наиболее совместимым с другими процессорами XSLT сериализацию фрагмента XML во временный файл, передачу URI этого файла в XSLT в качестве строкового параметра и оттуда загружать URI через документ XPath () функция.
Если вы посмотрите на документ JavaDoc, вы увидите, что он расширяет интерфейс Node, но не NodeList. Не уверен, что это сработает, но вы можете попробовать передать params.getChildNodes() вместо params.