Как передать документ XML в файл XSL с помощью API Javax.xml.transformer? - PullRequest
0 голосов
/ 24 января 2019

Я использую javax.xml.transform API для преобразования XSL. API позволяет применять только один XML-документ в качестве входных данных для применения преобразования, как показано ниже.

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

Кроме того, можно передавать простые строковые параметры, как показано ниже, без каких-либо проблем, как показано ниже:

transformer.setParameter("mode", "CREATE");

Но я хочу передать XML-документ в качестве параметра в файл XSL. Я попробовал приведенный ниже код, предложенный на одной из SO страниц, как показано ниже:

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

И я установил тег в XML для получения значения, как показано ниже:

<xsl:variable name="lookup" select="('documentFile')/>  . 

Но это не работает для меня. Может ли кто-нибудь помочь мне с правильной оплатой за передачу нескольких документов XML в любой файл XSL через API javax.xml.transform?

Обновление

Все еще не решен вопрос, может ли кто-нибудь разрешить мне передать объект XML в таблицу стилей XSLT 2.0 в качестве параметра. Я пробовал разные подходы, но все же не повезло. Мне нужно знать выход через API преобразования JAVA xsl.

Ответы [ 5 ]

0 голосов
/ 28 января 2019

Я думаю, что ваша проблема в XSLT. Изменение

<xsl:variable name="lookup" select="('documentFile')/>  . 

до

<xsl:variable name="lookup" select="document('lookup')/>

это заставит преобразователь сделать DOM вашего документа доступным в переменной lookup. Ключ lookup приходит от docs.put("lookup", documentFile);

Динамическая передача нескольких источников XML в XSL-преобразование через URIResolver.

Полный рабочий пример:

Там три файла XML: repo.xml, books.xml и articles.xml. repo.xml содержит информацию о статусе книг и статей. Файлы articles.xml и books.xml содержат информацию о заголовке каждого элемента. Цель состоит в том, чтобы напечатать информацию о статусе всех книг и статей вместе с информацией о названии. Записи во всех файлах связаны с помощью id ключей.

Найдите полный пример на github или скопируйте / вставьте списки ниже.

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>

books.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>

articles.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>

join.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>

Используйте этот код Java ...

@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);
    }
}

... чтобы произвести этот вывод

<?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>
0 голосов
/ 27 января 2019

Вы уже пробовали это?

org.w3c.dom.Document doc = ... // Your xml document
transformer.setParameter("demo", doc.getDocumentElement());
0 голосов
/ 26 января 2019

(Ответ расширен для обработки передачи проанализированного документа DOM W3C через URIResolver)

Это можно сделать в чистом XSLT / XPath с использованием версии Xalan XSLT-процессора, которая являетсяпоставляется с JRE.

В качестве примера, скажем, если имя одного из входных документов передается в качестве параметра в 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);

Этот параметр может затемпередается в функцию document() в XPath следующим образом:

<?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>

Это может прочитать basic.xml (из параметра):

<basic>
 <one/>
 <two/>
 <three/>
</basic>

и преобразовать его в:

<parent>
  <child>one</child>
  <child>two</child>
  <child>three</child>
</parent>

Параметром для функции document() является URI.Относительный путь разрешается относительно файла XSL.В равной степени это может быть полный URL-адрес или разрешено с помощью пользовательского transformer.setURIResolver(), как в вопросе.

(редактировать отсюда ...)

Для работы сПередача предварительно проанализированного объекта Document в XSLT, подход URIResolver может обработать передачу этого обратно функции document().

Например, с поиском в вопросе:

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

Этот XSL может выполнять итерацию того же basic.xml, как указано выше ...

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

... и выводить тот же результат.

0 голосов
/ 26 января 2019

Попробуйте заменить инструкцию <xsl:variable name="lookup" select="('documentFile')"/> на <xsl:variable name="documentFile" select="document($lookup)"/> и передать документ XML в качестве параметра с transformer.setParameter("lookup", "myfile.xml");, что означает: загрузить документ, на который ссылается параметр поиска, в переменную documentFile.

См. Также Извлечение данных из внешнего XML-файла с использованием XSL

0 голосов
/ 26 января 2019

Не уверен насчет вашей проблемы, если вы дадите вариант использования и спросите, как ее решить, вам будет полезно спросить, как исправить ваш код, поскольку у нас нет сквозной видимости вашего кода и XML.

Следующее может быть возможным решением:

1) Мы можем преобразовать xml в строку

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) Измените приведенный ниже код в соответствии с вашими требованиями, передаваемыми в виде списка объектов вызова для цикла.

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;
    }
}
...