Это можно сделать с помощью SAX ... Но я думаю, что более новый StAX (Streaming API for XML) будет лучше служить вашим целям.Вы можете создать XMLEventReader и использовать его для анализа вашего файла, определяя, какие узлы соответствуют одному из ваших критериев.Для простого выбора на основе пути (на самом деле это не XPath, а какой-то простой путь /
с разделителями) вам необходимо сохранить путь к текущему узлу, добавляя записи в строку для новых элементов или обрезая записи в конечном теге.Логического флага может быть достаточно для поддержания того, находитесь ли вы в «соответствующем режиме» или нет.
Когда вы получаете XMLEvents от вашего считывателя, вы можете скопировать соответствующие в XMLEventWriter что вы создали в некотором подходящем заполнителе, например StringWriter или ByteArrayOutputStream.После того, как вы завершили копирование некоторого XML-экстракта, который образует «поддокумент» того, для чего вы хотите создать DOM, просто предоставьте свой заполнитель для DocumentBuilder в подходящей форме.
Ограничение здесь в том, что вы не используете всю мощь языка XPath.Если вы хотите принять во внимание такие вещи, как положение узла, вы должны предвидеть это на своем пути.Возможно, кто-то знает о хорошем способе интеграции настоящей реализации XPath в это.
StAX действительно хорош тем, что дает вам контроль над синтаксическим анализом, а не использует какой-либо интерфейс обратного вызова через обработчик, такой как SAX.
Есть еще одна альтернатива: использование XSLT.Таблица стилей XSLT - это идеальный способ отфильтровать только релевантные материалы.Вы можете преобразовать свой ввод один раз, чтобы получить необходимые фрагменты и обработать их.Или запустите несколько таблиц стилей для одного и того же ввода, чтобы каждый раз получать нужный фрагмент.Однако еще более удачным (и более эффективным) решением было бы использование функций расширения и / или элементов расширения .
Функции расширения могут быть реализованы способом, независимым от XSLT.процессор используется.Они довольно просты в использовании в Java, и я точно знаю, что вы можете использовать их для передачи полных экстрактов XML в метод, потому что я уже сделал это.Может потребоваться несколько экспериментов, но это мощный механизм.Извлечение DOM (или узел), вероятно, является одним из приемлемых типов параметров для такого метода.Это оставило бы создание документа на процессоре XSLT, что еще проще.
Элементы расширения также очень полезны, но я думаю, что их нужно использовать в зависимости от реализации.Если у вас все в порядке с привязкой к определенной настройке JAXP, такой как Xerces + Xalan, они могут быть ответом.
При переходе на XSLT вы получите все преимущества полной реализации XPath 1.0, плюсдушевное спокойствие, которое приходит от знания XSLT, действительно хорошо в Java.Он ограничивает построение входного дерева теми узлами, которые необходимы в любое время, и работает быстро, потому что процессоры склонны компилировать таблицы стилей в байт-код Java, а не интерпретировать их.Впрочем, возможно, что использование компиляции вместо интерпретации теряет возможность использования элементов расширения.Не уверен насчет этого.Функции расширения все еще возможны.
Какой бы путь вы ни выбрали, для обработки XML в Java так много всего, что вы найдете много помощи в реализации этого, если вам не повезет найти готовыйрешение.Конечно, это было бы самым очевидным ... Не нужно изобретать велосипед, когда кто-то проделал тяжелую работу.
Удачи!
РЕДАКТИРОВАТЬ :потому что я на самом деле не чувствую депрессию на этот раз, вот демонстрация с использованием решения StAX, которое я взбил.Это, конечно, не самый чистый код, но он даст вам основную идею:
package staxdom;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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.StartElement;
import javax.xml.stream.events.XMLEvent;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
public class DOMExtractor {
private final Set<String> paths;
private final XMLInputFactory inputFactory;
private final XMLOutputFactory outputFactory;
private final DocumentBuilderFactory docBuilderFactory;
private final Stack<QName> activeStack = new Stack<QName>();
private boolean active = false;
private String currentPath = "";
public DOMExtractor(final Set<String> paths) {
this.paths = Collections.unmodifiableSet(new HashSet<String>(paths));
inputFactory = XMLInputFactory.newFactory();
outputFactory = XMLOutputFactory.newFactory();
docBuilderFactory = DocumentBuilderFactory.newInstance();
}
public void parse(final InputStream input) throws XMLStreamException, ParserConfigurationException, SAXException, IOException {
final XMLEventReader reader = inputFactory.createXMLEventReader(input);
XMLEventWriter writer = null;
StringWriter buffer = null;
final DocumentBuilder builder = docBuilderFactory.newDocumentBuilder();
XMLEvent currentEvent = reader.nextEvent();
do {
if(active)
writer.add(currentEvent);
if(currentEvent.isEndElement()) {
if(active) {
activeStack.pop();
if(activeStack.isEmpty()) {
writer.flush();
writer.close();
final Document doc;
final StringReader docReader = new StringReader(buffer.toString());
try {
doc = builder.parse(new InputSource(docReader));
} finally {
docReader.close();
}
//TODO: use doc
//Next bit is only for demo...
outputDoc(doc);
active = false;
writer = null;
buffer = null;
}
}
int index;
if((index = currentPath.lastIndexOf('/')) >= 0)
currentPath = currentPath.substring(0, index);
} else if(currentEvent.isStartElement()) {
final StartElement start = (StartElement)currentEvent;
final QName qName = start.getName();
final String local = qName.getLocalPart();
currentPath += "/" + local;
if(!active && paths.contains(currentPath)) {
active = true;
buffer = new StringWriter();
writer = outputFactory.createXMLEventWriter(buffer);
writer.add(currentEvent);
}
if(active)
activeStack.push(qName);
}
currentEvent = reader.nextEvent();
} while(!currentEvent.isEndDocument());
}
private void outputDoc(final Document doc) {
try {
final Transformer t = TransformerFactory.newInstance().newTransformer();
t.transform(new DOMSource(doc), new StreamResult(System.out));
System.out.println("");
System.out.println("");
} catch(TransformerException ex) {
ex.printStackTrace();
}
}
public static void main(String[] args) {
final Set<String> paths = new HashSet<String>();
paths.add("/root/one");
paths.add("/root/three/embedded");
final DOMExtractor me = new DOMExtractor(paths);
InputStream stream = null;
try {
stream = DOMExtractor.class.getResourceAsStream("sample.xml");
me.parse(stream);
} catch(final Exception e) {
e.printStackTrace();
} finally {
if(stream != null)
try {
stream.close();
} catch(IOException ex) {
ex.printStackTrace();
}
}
}
}
И файл sample.xml (должен быть в том же пакете):
<?xml version="1.0" encoding="UTF-8"?>
<root>
<one>
<two>this is text</two>
look, I can even handle mixed!
</one>
... not sure what to do with this, though
<two>
<willbeignored/>
</two>
<three>
<embedded>
<and><here><we><go>
Creative Commons Legal Code
Attribution 3.0 Unported
CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR
DAMAGES RESULTING FROM ITS USE.
License
THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE
COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY
COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED.
BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE
TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY
BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS
CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.
</go></we></here></and>
</embedded>
</three>
</root>
РЕДАКТИРОВАТЬ 2: Только что заметил в ответе Блеза Дафана, что есть источник StAXSource.Это будет еще эффективнее.Используйте это, если вы собираетесь с StAX.Устранит необходимость сохранять некоторый буфер.StAX позволяет вам «заглядывать» при следующем событии, поэтому вы можете проверить, является ли это стартовый элемент с правильным путем, не используя его, прежде чем передавать его в преобразователь.