Ускорение xpath
У меня есть документ с 1000 документами, формат которого похож на
<Example>
<Entry>
<n1></n1>
<n2></n2>
</Entry>
<Entry>
<n1></n1>
<n2></n2>
</Entry>
<!--and so on-->
Здесь имеется более 1000 узлов ввода. Я пишу программу Java, которая в основном получает все node по одному и анализирует каждый node. Но проблема в том, что время поиска узлов увеличивается с его no. Например, для получения второго node 100 мс для получения второго требуется 78 миллисекунд, и он продолжает увеличиваться. А для получения 999 node требуется больше 5 секунд. Это очень медленно. Мы будем подключать этот код к файлам XML, которые имеют даже более 1000 записей. Некоторые вроде миллионов. Общее время для анализа всего документа составляет более 5 минут.
Я использую этот простой код для его перемещения. Здесь nxp
- мой собственный класс, который имеет все методы для получения узлов из xpath.
nxp.fromXpathToNode("/Example/Entry" + "[" + i + "]", doc);
и doc
- это документ для файла. i
- это no из node для извлечения.
Также, когда я пытаюсь что-то вроде этого
List<Node> nl = nxp.fromXpathToNodes("/Example/Entry",doc);
content = nl.get(i);
Я сталкиваюсь с той же проблемой.
У любого есть решение о том, как ускорить tretirival узлов, поэтому требуется один и тот же промежуток времени, чтобы получить 1-й node, а также 1000 node из файла XML.
Спасибо
вот код для xpathtonode.
public Node fromXpathToNode(String expression, Node context)
{
try
{
return (Node)this.getCachedExpression(expression).evaluate(context, XPathConstants.NODE);
}
catch (Exception cause)
{
throw new RuntimeException(cause);
}
}
и вот код для из-квантонодов.
public List<Node> fromXpathToNodes(String expression, Node context)
{
List<Node> nodes = new ArrayList<Node>();
NodeList results = null;
try
{
results = (NodeList)this.getCachedExpression(expression).evaluate(context, XPathConstants.NODESET);
for (int index = 0; index < results.getLength(); index++)
{
nodes.add(results.item(index));
}
}
catch (Exception cause)
{
throw new RuntimeException(cause);
}
return nodes;
}
и вот стартовый
открытый класс NativeXpathEngine реализует XpathEngine
{
закрытый конечный XPathFactory factory;
private final XPath engine;
/**
* Cache for previously compiled XPath expressions. {@link XPathExpression#hashCode()}
* is not reliable or consistent so use the textual representation instead.
*/
private final Map<String, XPathExpression> cachedExpressions;
public NativeXpathEngine()
{
super();
this.factory = XPathFactory.newInstance();
this.engine = factory.newXPath();
this.cachedExpressions = new HashMap<String, XPathExpression>();
}
Ответы
Ответ 1
Попробуйте VTD-XML. Он использует меньше памяти, чем DOM. Он проще в использовании, чем SAX, и поддерживает XPath. Вот пример кода, который поможет вам начать работу. Он применяет XPath для получения элементов Entry, а затем распечатывает дочерние элементы n1 и n2.
final VTDGen vg = new VTDGen();
vg.parseFile("/path/to/file.xml", false);
final VTDNav vn = vg.getNav();
final AutoPilot ap = new AutoPilot(vn);
ap.selectXPath("/Example/Entry");
int count = 1;
while (ap.evalXPath() != -1) {
System.out.println("Inside Entry: " + count);
//move to n1 child
vn.toElement(VTDNav.FIRST_CHILD, "n1");
System.out.println("\tn1: " + vn.toNormalizedString(vn.getText()));
//move to n2 child
vn.toElement(VTDNav.NEXT_SIBLING, "n2");
System.out.println("\tn2: " + vn.toNormalizedString(vn.getText()));
//move back to parent
vn.toElement(VTDNav.PARENT);
count++;
}
Ответ 2
Правильное решение - отсоединить node сразу после вызова элемента (i), например:
Node node = results.item(index)
node.getParentNode().removeChild(node)
nodes.add(node)
См. XPath.evaluate производительность замедляется (абсурдно) по нескольким вызовам
Ответ 3
У меня была аналогичная проблема с оценкой Xpath, я попытался использовать CachedXPathAPI, который быстрее на 100X, чем XPathApis, который использовался ранее.
Дополнительная информация об этом Api предоставляется здесь:
http://xml.apache.org/xalan-j/apidocs/org/apache/xpath/CachedXPathAPI.html
Надеюсь, это поможет.
Ура,
Madhusudhan
Ответ 4
Какой парсер вы используете?
DOM извлекает весь документ в памяти - как только вы вытаскиваете весь документ в памяти, ваши операции могут быть быстрыми, но это может произойти в веб-приложении или в цикле for.
САКС-парсер выполняет по запросу синтаксический анализ и загрузку узлов по мере запроса.
Поэтому попробуйте использовать реализацию парсера, которая вам подходит.
Ответ 5
Если вам нужно разбирать огромные, но плоские документы, SAX - хорошая альтернатива. Это позволяет обрабатывать XML как поток, а не создавать огромные DOM. Ваш пример может быть проанализирован с помощью ContentHandler следующим образом:
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.ext.DefaultHandler2;
public class ExampleHandler extends DefaultHandler2 {
private StringBuffer chars = new StringBuffer(1000);
private MyEntry currentEntry;
private MyEntryHandler myEntryHandler;
ExampleHandler(MyEntryHandler myEntryHandler) {
this.myEntryHandler = myEntryHandler;
}
@Override
public void characters(char[] ch, int start, int length)
throws SAXException {
chars.append(ch);
}
@Override
public void endElement(String uri, String localName, String qName)
throws SAXException {
if ("Entry".equals(localName)) {
myEntryHandler.handle(currentEntry);
currentEntry = null;
}
else if ("n1".equals(localName)) {
currentEntry.setN1(chars.toString());
}
else if ("n2".equals(localName)) {
currentEntry.setN2(chars.toString());
}
}
@Override
public void startElement(String uri, String localName, String qName,
Attributes atts) throws SAXException {
chars.setLength(0);
if ("Entry".equals(localName)) {
currentEntry = new MyEntry();
}
}
}
Если документ имеет более глубокую и сложную структуру, вам понадобится использовать Stacks для отслеживания текущего пути в документе. Затем вам следует подумать о том, чтобы написать общий контент ContentHandler для выполнения грязной работы и использовать с обработчиками, зависящими от типа документа.
Ответ 6
Используйте библиотеку JAXEN для xpaths:
http://jaxen.codehaus.org/