Como passar um documento XML para o arquivo XSL usando Javax.xml.API do transformador?
estou a usar javax.xml.transform
a API para fazer a transformação XSL . A API apenas permite um documento XML como uma entrada para aplicar a transformação como abaixo .
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
StringWriter stringWriter = new StringWriter();
File xml = new File("C:\\abc");
File xsl = new File("C:\\def.xsl");
factory.setNamespaceAware(true);
DocumentBuilder builder = factory.newDocumentBuilder();
document = builder.parse(xml);
TransformerFactory transformerFactory =
TransformerFactory.newInstance();
StreamSource style = new StreamSource(xsl);
Transformer transformer = transformerFactory.newTransformer(style);
DOMSource source = new DOMSource(document);
além disso, pode passar por parâmetros simples como abaixo, sem qualquer problema como abaixo:
transformer.setParameter("mode", "CREATE");
mas, quero passar um documento XML como parâmetro para o ficheiro XSL . Eu tentei abaixo do código como sugerido em uma das páginas do SO, como abaixo:
DocumentBuilder builder = factory.newDocumentBuilder();
final Document documentFile = builder.parse(xml2);
Map<String, Document> docs = new HashMap<String, Document>();
docs.put("lookup", documentFile);
transformer.setURIResolver(new DocumentURIResolver(docs));
e eu defini, a marca em XML para receber o valor abaixo :
<xsl:variable name="lookup" select="('documentFile')/> .
Mas não funciona para mim . Qualquer um pode me ajudar com o pagamento correto para passar vários documentos XML para qualquer arquivo XSL via javax.xml.transformar API ?
actualizar
ainda preso com o problema, qualquer um pode me deixar como posso passar objeto XML em uma folha de estilo XSLT 2.0 como um param . Tentei abordagens diferentes, mas ainda não tive sorte . Preciso de saber uma saída através da API JAVA xsl transform .
5 answers
<xsl:variable name="lookup" select="('documentFile')/> .
A
<xsl:variable name="lookup" select="document('lookup')/>
Isto fará com que o transformador torne o DOM
do seu documento acessível na variável lookup
. A chave lookup
vem de docs.put("lookup", documentFile);
Passa dinamicamente várias fontes XML para a transformação XSL via URIResolver.
Exemplo Completo De Funcionamento:
Esteja lá três ficheiros XML.:repo.xml
, books.xml
e articles.xml
O repo.xml
contém informações sobre o estado dos livros e artigo. Os ficheiros articles.xml
e books.xml
contêm informações do título sobre cada item. O objetivo é imprimir informações de status de todos os livros e artigos, juntamente com a informação do título. Os itens em todos os ficheiros estão ligados através das teclas id
.
Encontre um exemplo completo em github ou copie / cole as listagens abaixo.
Repo.xml<repository>
<book>
<id>1</id>
<status>available</status>
</book>
<book>
<id>2</id>
<status>lost</status>
</book>
<article>
<id>1</id>
<status>in transit</status>
</article>
</repository>
Livros.xml
<books>
<book id="1">
<title>Book One</title>
</book>
<book id="2">
<title>Book Two</title>
</book>
<book id="3">
<title>Book Three</title>
</book>
</books>
Artigos.xml
<articles>
<article id="1">
<title>Article One</title>
</article>
<article id="2">
<title>Article Two</title>
</article>
<article id="3">
<title>Article Three</title>
</article>
</articles>
Junta-te.xsl
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="UTF-8" indent="yes" />
<xsl:template match="/">
<titleStatusJoin>
<xsl:for-each select="//book">
<xsl:variable name="myId" select="id" />
<book>
<status>
<xsl:value-of select="status" />
</status>
<title>
<xsl:for-each select="document('bookFile')//book">
<xsl:variable name="bookId" select="@id" />
<xsl:choose>
<xsl:when test="$myId = $bookId">
<xsl:value-of select="title" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</title>
</book>
</xsl:for-each>
<xsl:for-each select="//article">
<xsl:variable name="myId" select="id" />
<article>
<status>
<xsl:value-of select="status" />
</status>
<title>
<xsl:for-each select="document('articleFile')//article">
<xsl:variable name="bookId" select="@id" />
<xsl:choose>
<xsl:when test="$myId = $bookId">
<xsl:value-of select="title" />
</xsl:when>
</xsl:choose>
</xsl:for-each>
</title>
</article>
</xsl:for-each>
</titleStatusJoin>
</xsl:template>
</xsl:stylesheet>
Usa este Java codigo...
@Test
public void useMultipleXmlSourcesInOneXsl3() {
InputStream xml = Thread.currentThread().getContextClassLoader().getResourceAsStream("stack54335576/repo.xml");
InputStream xsl = Thread.currentThread().getContextClassLoader().getResourceAsStream("stack54335576/join3.xsl");
InputStream booksXml = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("stack54335576/books.xml");
InputStream articlesXml = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("stack54335576/articles.xml");
Document booksDom = readXml(booksXml);
Document articlesDom = readXml(articlesXml);
Map<String, Document> parameters = new HashMap<>();
parameters.put("bookFile", booksDom);
parameters.put("articleFile", articlesDom);
xslt(xml, xsl, parameters);
}
public final void xslt(InputStream xml, InputStream xsl, Map<String, Document> parameters) {
try {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(xsl));
transformer.setURIResolver((href, base) -> new DOMSource(parameters.get(href)));
transformer.transform(new StreamSource(xml), new StreamResult(System.out));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private Document readXml(InputStream xmlin) {
try {
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
DocumentBuilder db = dbf.newDocumentBuilder();
return db.parse(xmlin);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
... para produzir esta produção
<?xml version="1.0" encoding="UTF-8"?>
<titleStatusJoin>
<book>
<status>available</status>
<title>Book One</title>
</book>
<book>
<status>lost</status>
<title>Book Two</title>
</book>
<article>
<status>in transit</status>
<title>Article One</title>
</article>
</titleStatusJoin>
org.w3c.dom.Document doc = ... // Your xml document
transformer.setParameter("demo", doc.getDocumentElement());
(resposta expandida para lidar com a passagem em um documento W3C DOM processado através de um URIResolver)
Isto pode ser feito em puro XSLT/XPath usando a versão do processador Xalan XSLT que é fornecido com a JRE.
Como exemplo, diga se o nome de um dos documentos de entrada é passado como um parâmetro para o Transformer
:
File parentDir = new File("c:\\dir");
StringWriter stringWriter = new StringWriter();
File xml = new File(parentDir, "input.xml");
File xsl = new File(parentDir, "xslt-document-param.xslt");
Source xsltSource = new StreamSource(xsl);
Source xmlSource = new StreamSource(xml);
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer(xsltSource);
transformer.setParameter("doc-name", "basic.xml");
transformer.transform(xmlSource, new StreamResult(stringWriter));
System.out.println(stringWriter);
Este parâmetro pode então ser passado para a função document()
em XPath da seguinte forma:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xalan="http://xml.apache.org/xalan"
version="1.0"
exclude-result-prefixes="xalan">
<xsl:output
method="xml"
indent="yes"
xalan:indent-amount="2"/>
<xsl:param name="doc-name"/>
<xsl:template match="/">
<xsl:variable name="doc-content" select="document($doc-name)"/>
<parent>
<xsl:for-each select="$doc-content/basic/*">
<child>
<xsl:value-of select="name(.)"/>
</child>
</xsl:for-each>
</parent>
</xsl:template>
</xsl:stylesheet>
Isto é capaz de ler isto. basic.xml
(a partir do parâmetro):
<basic>
<one/>
<two/>
<three/>
</basic>
E transforma-o em:
<parent>
<child>one</child>
<child>two</child>
<child>three</child>
</parent>
O parâmetro da função document()
é um URI. Um caminho relativo é resolvido em relação ao ficheiro XSL. Da mesma forma, este poderia ser um URL completo, ou resolvido através de um personalizado transformer.setURIResolver()
como na pergunta.
(edite a partir daqui...)
Para trabalhar com a passagem de um objeto pré-analisado Document
para o XSLT, a aproximação URIResolver
é capaz de lidar com a passagem de volta para a função document()
.
Para exemplo, com a pesquisa na pergunta:
File lookupXml = new File(parentDir, "basic.xml");
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document document = builder.parse(lookupXml);
Map<String, Document> docs = new HashMap<>();
docs.put("lookup", document);
transformer.setURIResolver((href, base) -> new DOMSource(docs.get(href)));
Este XSL é capaz de iterar através do mesmo basic.xml
como acima...
<xsl:template match="/">
<xsl:variable name="doc-content" select="document('lookup')"/>
<parent>
<xsl:for-each select="$doc-content/basic/*">
<child>
<xsl:value-of select="name(.)"/>
</child>
</xsl:for-each>
</parent>
</xsl:template>
... e produz o mesmo resultado.
Não tenho certeza sobre o seu problema, se você der usecase e perguntar como resolver será mais útil instado a perguntar como corrigir o seu código, uma vez que não estamos tendo fim para acabar com a visiblity do seu código e xml.
A seguinte solução pode ser possível:
1) podemos converter xml para string
try {
StringReader _reader = new StringReader("<xml>vkhan</xml>");
StringWriter _writer = new StringWriter();
TransformerFactory tFactory = TransformerFactory.newInstance();
Transformer transformer = tFactory.newTransformer(
new javax.xml.transform.stream.StreamSource("styler.xsl"));//ur xsl
transformer.transform(
new javax.xml.transform.stream.StreamSource(_reader),
new javax.xml.transform.stream.StreamResult(_writer));
String result = writer.toString();
} catch (Exception e) {
e.printStackTrace();
}
2) modifique abaixo o código de acordo com a sua requisição passar como Lista de objeto de chamada para loop.
public class Data {
public static final Document transformXmlDocument(Document sourceDocument, InputStream xsltFile) {
DOMSource xmlSource = new DOMSource(sourceDocument);
StreamSource xsltSource = new StreamSource(xsltFile);
Document transformedData = null;
try {
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(xsltSource);
ByteArrayOutputStream output = new ByteArrayOutputStream();
StreamResult result = new StreamResult(output);
transformer.transform(xmlSource, result);
DocumentBuilder resultBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
transformedData = resultBuilder.parse(
new InputSource(
new StringReader(
new String(output.toByteArray())
)
)
);
} catch (Exception e) {
Log.e("XSLT Transformation", e.getMessage());
}
return transformedData;
}
}
Tente substituir a sua instrução <xsl:variable name="lookup" select="('documentFile')"/>
por <xsl:variable name="documentFile" select="document($lookup)"/>
e passe o seu documento XML como um parâmetro com transformer.setParameter("lookup", "myfile.xml");
o que significa : carregue o documento referenciado pelo parâmetro procurar na variável documentFile
.
Ver também extrair dados do ficheiro XML externo utilizando XSL