Эффективный конвейер XSLT в Java (или перенаправление результатов на источники) - PullRequest
19 голосов
/ 21 августа 2009

У меня есть серия таблиц стилей XSL 2.0, которые вставляются друг в друга, то есть выходные данные таблицы стилей A подают каналы B, а каналы C.

Какой самый эффективный способ сделать это? Перефразируемый вопрос таков: как можно эффективно направить результаты одного преобразования в другое.

Вот моя первая попытка:

@Override
public void transform(Source data, Result out) throws TransformerException{
    for(Transformer autobot : autobots){
        if(autobots.indexOf(autobot) != (autobots.size()-1)){
            log.debug("Transforming prelim stylesheet...");
            data = transform(autobot,data);
        }else{
            log.debug("Transforming final stylesheet...");
            autobot.transform(data, out);
        }
    }
}

private Source transform(Transformer autobot, Source data) throws TransformerException{
    DOMResult result = new DOMResult();
    autobot.transform(data, result);
    Node node = result.getNode();
    return new DOMSource(node);
}

Как видите, я использую DOM, чтобы сидеть между преобразованиями, и хотя это удобно, это неоптимальная производительность.

Есть ли какой-нибудь простой способ указать, направить ли SAXResult к SAXSource? Решение StAX было бы другим вариантом.

Мне известны такие проекты, как XProc , что очень здорово, если вы еще не взглянули на них, но я не хотел вкладывать средства в целую среду.

Ответы [ 3 ]

23 голосов
/ 24 августа 2009

Я нашел это: # 3. Цепные преобразования , которые показывают два способа использования TransformerFactory для цепочки преобразований, при этом результаты одного преобразования передают следующее преобразование, а затем, наконец, выводятся в систему. Это исключает необходимость промежуточной сериализации в строку, файл и т. Д. Между преобразованиями.

Когда несколько, последовательно преобразования необходимы для тот же XML-документ, обязательно избегайте ненужные операции разбора. я часто сталкиваются с кодом, который преобразует строку в другую строку, затем преобразует эту строку в еще другая строка Это не только медленно, но это может потреблять значительное объем памяти, особенно если промежуточные строки не разрешено собирать мусор.

Большинство преобразований основаны на серия SAX событий. SAX-парсер будет обычно анализировать InputStream или другой InputSource в события SAX, который затем может быть подан на Трансформатор. Вместо того, чтобы иметь Вывод трансформатора в файл, строку, или другой такой результат, SAXResult может быть использован вместо SAXResult принимает ContentHandler, который может передать эти события SAX непосредственно другой трансформатор и т. д.

Вот один подход, и тот, который я обычно предпочитают, поскольку это обеспечивает больше гибкость для различного ввода и выходные источники. Это также делает это довольно легко создать преобразование цепочка динамически и с переменной количество преобразований.

SAXTransformerFactory stf = (SAXTransformerFactory)TransformerFactory.newInstance();

// These templates objects could be reused and obtained from elsewhere.
Templates templates1 = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("MyStylesheet1.xslt")));
Templates templates2 = stf.newTemplates(new StreamSource(
  getClass().getResourceAsStream("MyStylesheet1.xslt")));

TransformerHandler th1 = stf.newTransformerHandler(templates1);
TransformerHandler th2 = stf.newTransformerHandler(templates2);

th1.setResult(new SAXResult(th2));
th2.setResult(new StreamResult(System.out));

Transformer t = stf.newTransformer();
t.transform(new StreamSource(System.in), new SAXResult(th1));

// th1 feeds th2, which in turn feeds System.out.
2 голосов
/ 01 марта 2013

Смежный вопрос Эффективный конвейер XSLT с параметрами в Java уточнил правильные параметры, передаваемые в такую ​​цепочку трансформатора.

И он также дал подсказку о несколько более коротком решении без третьего преобразователя:

SAXTransformerFactory stf = (SAXTransformerFactory)TransformerFactory.newInstance();

Templates templates1 = stf.newTemplates(new StreamSource(
        getClass().getResourceAsStream("MyStylesheet1.xslt")));
Templates templates2 = stf.newTemplates(new StreamSource(
        getClass().getResourceAsStream("MyStylesheet2.xslt")));

TransformerHandler th1 = stf.newTransformerHandler(templates1);
TransformerHandler th2 = stf.newTransformerHandler(templates2);

th2.setResult(new StreamResult(System.out));

// Note that indent, etc should be applied to the last transformer in chain:
th2.getTransformer().setOutputProperty(OutputKeys.INDENT, "yes");

th1.getTransformer().transform(new StreamSource(System.in), new SAXResult(th2));
2 голосов
/ 21 августа 2009

Лучше всего придерживаться DOM, как вы делаете, потому что XSLT-процессор должен был бы построить дерево в любом случае - потоковая передача является лишь вариантом для очень ограниченной категории преобразований, и немногие, если какие-либо процессоры могут это понять автоматически и переключиться на реализацию только для потоковой передачи; в противном случае они просто читают ввод и строят дерево.

...