Можно ли создать выходной поток XSL-трансформатора? - PullRequest
1 голос
/ 14 июня 2019

Можно ли создать TransformerOutputStream, который расширяет стандарт java.io.OutputStream, оборачивает предоставленный выходной поток и применяет XSL-преобразование?Я не могу найти какую-либо комбинацию API, которая бы позволяла мне это делать.

Ключевым моментом является то, что после создания TransformerOutputStream может быть передано другим API, которые принимают стандарт java.io.OutputStream.

Минимальное использование будет примерно таким:

java.io.InputStream in = getXmlInput();
java.io.OutputStream out = getTargetOutput();

javax.xml.transform.Templates templates = createReusableTemplates();        // could also use S9API
TransformerOutputStream tos = new TransformerOutputStream(out, templates);  // extends OutputStream

com.google.common.io.ByteStreams.copy(in, tos);

// possibly flush/close tos if required by implementation

Это пример JAXP, но, поскольку я сейчас использую Saxon, решение S9API тоже подойдет.

ОсновноеПроспект, который я убедил, выглядит следующим образом:

  • класс, который расширяет java.io.OutputStream и реализует org.xml.sax.ContentHandler
  • XSL-трансформатор на основе org.xml.sax.ContentHandler

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

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

Я написал и в настоящее время использую класс, который предоставляет этот интерфейс, но он просто собирает выходные данные во внутренний буфер, а затем использует стандартныйJAXP StreamSource, чтобы прочитать это при сбросе или закрытии, поэтому заканчивается буферизация всего документа.

1 Ответ

2 голосов
/ 14 июня 2019

Вы можете заставить ваш TransformerOutputStream расширять ByteArrayOutputStream, а его метод close () может принимать базовый массив byte [], оборачивать его в ByteArrayInputStream и вызывать преобразование со входными данными, взятыми из этого InputStream.

Но, похоже, вы также хотите избежать помещения всего содержимого потока в память. Итак, давайте предположим, что преобразование, которое вы хотите применить, является преобразованием XSLT 3.0. К сожалению, хотя Saxon как потоковый XSLT-преобразователь работает в основном в режиме push (под «push» я подразумеваю, что поставщик данных вызывает потребителя данных, тогда как «pull» означает, что потребитель данных вызывает поставщика данных), на первом этапе чтение и синтаксический анализ ввода всегда выполняется в режиме извлечения - я не знаю парсера XML, в который можно вставить лексический ввод XML.

Это означает, что здесь есть двухтактный конфликт. Есть два решения для конфликта двухтактных. Один из них заключается в том, чтобы буферизовать данные в памяти (это был упомянутый ранее подход ByteArrayOutputStream). Другой - использовать два потока, один из которых записывает в общий буфер, а другой читает из него. Этого можно достичь, используя PipedOutputStream в потоке записи (https://docs.oracle.com/javase/8/docs/api/index.html?java/io/PipedOutputStream.html) и PipedInputStream в потоке чтения.

Предостережение: Я на самом деле не пробовал это, но я не вижу причин, почему это не должно работать.

Обратите внимание, что тема потоковой передачи в XSLT 3.0 довольно сложна; вам нужно будет узнать об этом, прежде чем вы сможете добиться большого прогресса здесь. Я бы начал с выступления Абеля Брааксмы из XML London 2014: https://xmllondon.com/2014/presentations/braaksma

...