Проблема вы» что при создании StAXSource
ваше START_ELEMENT событие уже было использовано. Таким образом, XMLEventReader
, вероятно, находится на каком-то событии текстового узла в виде пробела или что-то еще, что не может быть источником XML-документа. Вы можете использовать метод peek()
для просмотра следующего события без его использования. Однако убедитесь, что есть событие с hasNext()
.
Я не уверен на 100% того, чего вы хотите достичь, поэтому вот некоторые вещи, которые вы могли бы сделать в зависимости от сценария.
EDIT: Я просто прочитал некоторые комментарии по вашему вопросу, которые делают вещи более понятными. Нижеследующее может помочь вам достичь желаемого результата с некоторой корректировкой. Также обратите внимание, что Java XSLT-процессоры позволяют использовать функции расширения и элементы расширения, которые могут вызывать Java-код из таблицы стилей XSLT. Это может быть мощным способом расширения базовых функций XSLT с помощью внешних ресурсов, таких как запросы к базе данных.
В случае, если вы хотите, входной XML должны быть преобразованы в один выходной XML, вы могли бы быть лучше просто с помощью преобразования XML стилей. В вашем коде вы создаете трансформатор без каких-либо шаблонов, поэтому он становится стандартным «преобразователем идентичности», который просто копирует входные данные для вывода. Предположим, что ваш входной XML выглядит следующим образом:
<?xml version="1.0" encoding="UTF-8"?>
<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml/3.2" gml:id="featureCollection" xmlns:eg="acme.com">
<gml:featureMember>
<eg:RST/>
<eg:pole>Krakow</eg:pole>
<eg:localId>id1234</eg:localId>
</gml:featureMember>
<gml:featureMember>
<eg:RST>1002</eg:RST>
<eg:pole>Rzeszow</eg:pole>
<eg:localId>id1235</eg:localId>
</gml:featureMember>
</gml:FeatureCollection>
Я связан префикс eg
к некоторому фиктивного пространства имен, так как он отсутствует в образце и установил некорректный элемент RST.
Следующая программа будет запускать преобразование XSLT на вашем входе и записывать его в выходной файл.
package xsltplayground;
import java.io.File;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
public class XSLTplayground {
public static void main(String[] args) throws Exception {
URL url = XSLTplayground.class.getResource("sample.xml");
File input = new File(url.toURI());
URL url2 = XSLTplayground.class.getResource("stylesheet.xsl");
File xslt = new File(url2.toURI());
URL url3 = XSLTplayground.class.getResource(".");
File output = new File(new File(url3.toURI()), "output.xml");
change(input, output, xslt);
}
private static void change(File pathIn, File pathOut, File xsltFile) {
try {
// Creating transformer with XSLT file
TransformerFactory tf = TransformerFactory.newInstance();
Source xsltSource = new StreamSource(xsltFile);
Transformer t = tf.newTransformer(xsltSource);
// Input source
Source input = new StreamSource(pathIn);
// Output target
Result output = new StreamResult(pathOut);
// Transforming
t.transform(input, output);
} catch (TransformerConfigurationException ex) {
Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
} catch (TransformerException ex) {
Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
Вот stylesheet.xsl пример файла, который для удобства я просто сбрасывали в том же пакете, что и входной XML и класса.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:eg="acme.com">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="node()|@*">
<xsl:copy>
<xsl:apply-templates select="node()|@*" />
</xsl:copy>
</xsl:template>
<xsl:template match="gml:featureMember">
<gml:member>
<xsl:apply-templates select="node()|@*" />
</gml:member>
</xsl:template>
</xsl:stylesheet>
выше таблица стилей будет копировать все по умолчанию, но когда дело доходит до <gml:featureMember>
элемента будет обернуть содержимое в новый <gml:member>
элемент. Просто очень простой пример того, что вы можете сделать с XSLT.
Выход будет:
<?xml version="1.0" encoding="UTF-8"?>
<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml/3.2" xmlns:eg="acme.com" gml:id="featureCollection">
<gml:member>
<eg:RST/>
<eg:pole>Krakow</eg:pole>
<eg:localId>id1234</eg:localId>
</gml:member>
<gml:member>
<eg:RST>1002</eg:RST>
<eg:pole>Rzeszow</eg:pole>
<eg:localId>id1235</eg:localId>
</gml:member>
</gml:FeatureCollection>
Так как вход и выход потока файлов, вам не нужен весь DOM в памяти. XSLT в Java довольно быстро и эффективно, поэтому этого может быть достаточно.
Возможно, вы действительно хотите разбить каждое вхождение какого-либо элемента в свой собственный выходной файл с некоторыми изменениями в нем. Вот пример кода, который использует StAX для разделения элементов <gml:featureMember>
как отдельных документов. Затем вы можете перебирать созданные файлы и преобразовывать их, но вы хотите (XSLT снова будет хорошим выбором). Очевидно, что обработка ошибок должна быть немного более надежной. Это просто для демонстрации.
package xsltplayground;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.stream.XMLEventFactory;
import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLEventWriter;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.stream.StreamSource;
public class XSLTplayground {
public static void main(String[] args) throws Exception {
URL url = XSLTplayground.class.getResource("sample.xml");
File input = new File(url.toURI());
URL url2 = XSLTplayground.class.getResource("stylesheet.xsl");
File xslt = new File(url2.toURI());
URL url3 = XSLTplayground.class.getResource(".");
File output = new File(url3.toURI());
change(input, output, xslt);
}
private static void change(File pathIn, File directoryOut, File xsltFile) throws InterruptedException {
try {
// Creating a StAX event reader from the input
XMLInputFactory xmlIf = XMLInputFactory.newFactory();
XMLEventReader reader = xmlIf.createXMLEventReader(new StreamSource(pathIn));
// Create a StAX output factory
XMLOutputFactory xmlOf = XMLOutputFactory.newInstance();
int counter = 1;
// Keep going until no more events
while (reader.hasNext()) {
// Peek into the next event to find out what it is
XMLEvent next = reader.peek();
// If it's the start of a featureMember element, commence output
if (next.isStartElement()
&& next.asStartElement().getName().getLocalPart().equals("featureMember")) {
File output = new File(directoryOut, "output_" + counter + ".xml");
try (OutputStream ops = new FileOutputStream(output)) {
XMLEventWriter writer = xmlOf.createXMLEventWriter(ops);
copy(reader, writer);
writer.flush();
writer.close();
}
counter++;
} else {
// Not in a featureMember element: ignore
reader.next();
}
}
} catch (XMLStreamException ex) {
Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
} catch (IOException ex) {
Logger.getLogger(XSLTplayground.class.getName()).log(Level.SEVERE, null, ex);
}
}
private static void copy(XMLEventReader reader, XMLEventWriter writer) throws XMLStreamException {
// Creating an XMLEventFactory
XMLEventFactory ef = XMLEventFactory.newFactory();
// Writing an XML document start
writer.add(ef.createStartDocument());
int depth = 0;
boolean stop = false;
while (!stop) {
XMLEvent next = reader.nextEvent();
writer.add(next);
if (next.isStartElement()) {
depth++;
} else if (next.isEndElement()) {
depth--;
if (depth == 0) {
writer.add(ef.createEndDocument());
stop = true;
}
}
}
}
}
Вы рассматривали ее решение с потоковым XSLT 3.0, который поддерживается Saxon 9 EE от saxonica.com? Если вы отредактируете свой вопрос и покажете нам, какое изменение вы хотите реализовать на элементах 'featureMember', и хотите ли вы генерировать единый результат со всеми измененными элементами или новым документом для каждого измененного элемента, тогда я мог бы показать вам, как это сделать с XSLT 3.0 декларативным способом. –
Я ничего не знаю о XSLT 3.0, поэтому я не рассматривал его. Я не могу сказать, что я хочу изменить на элементах 'featureMember' - это зависит от пользователя (вот почему мне нужен DOM здесь). Я должен найти конкретный 'featureMember' своим локальным идентификатором и изменить некоторые его элементы. У меня есть таблица с изменениями, которые я хочу сделать (они не всегда одинаковы). Я хочу создать единый результат со всеми измененными элементами. – sophiess
Да, мне интересно. Я уже сделал это как-то (это работает, но слишком медленно - 1 час в файле). Если это действительно эффективнее, я могу воспользоваться некоторой помощью. – sophiess