Ускорение XPath - PullRequest
       30

Ускорение XPath

18 голосов
/ 02 марта 2010

У меня есть 1000 документов, формат которых выглядит примерно так:

<Example>
     <Entry>
          <n1></n1>
          <n2></n2>
      </Entry>
      <Entry>
          <n1></n1>
          <n2></n2>
      </Entry>
      <!--and so on-->

Здесь более 1000 узлов входа. Я пишу Java-программу, которая в основном получает все узлы один за другим и проводит анализ каждого узла. Но проблема в том, что время поиска узлов увеличивается с его нет. Например, для извлечения первого узла требуется 78 миллисекунд, а для извлечения второго - 100 мс, и он продолжает увеличиваться. И для получения узла 999 требуется более 5 секунд. Это очень медленно. Мы будем подключать этот код к XML-файлам, которые содержат даже более 1000 записей. Некоторым нравятся миллионы. Общее время анализа всего документа составляет более 5 минут.

Я использую этот простой код для его обхода. Здесь nxp - мой собственный класс, в котором есть все методы для получения узлов из xpath.

nxp.fromXpathToNode("/Example/Entry" + "[" + i  + "]", doc);    

и doc - документ для файла. i - это номер узла для извлечения.

Также, когда я пытаюсь что-то вроде этого

List<Node> nl = nxp.fromXpathToNodes("/Example/Entry",doc);  
      content = nl.get(i);    

У меня такая же проблема.

У каждого есть какое-либо решение о том, как ускорить третичное сравнение узлов, поэтому для получения 1-го узла, а также 1000-го узла из файла 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);  
    }  
}  

и вот код для xpathtonodes.

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;

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>();  
}  

Ответы [ 6 ]

10 голосов
/ 10 августа 2010

Попробуйте 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++;
}
5 голосов
/ 27 февраля 2013

Правильным решением является отсоединение узла сразу после вызова item (i), например так:

Node node = results.item(index)
node.getParentNode().removeChild(node)
nodes.add(node)

См. Производительность XPath.evaluate замедляется (нелепо) при нескольких вызовах

3 голосов
/ 12 марта 2012

У меня была похожая проблема с оценкой Xpath, я пытался использовать CachedXPathAPI, который в 100 раз быстрее, чем XPathApi, который использовался ранее. больше информации об этом Api предоставлено здесь: http://xml.apache.org/xalan-j/apidocs/org/apache/xpath/CachedXPathAPI.html

Надеюсь, это поможет. Ура, Madhusudhan

2 голосов
/ 02 марта 2010

Если вам нужно разобрать огромные, но плоские документы, 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();
        }
    }
}

Если документ имеет более глубокую и сложную структуру, вам потребуется использовать стеки для отслеживания текущего пути в документе. Тогда вам следует подумать о написании ContentHandler общего назначения, чтобы выполнить грязную работу и использовать с зависимыми от типа документа обработчиками.

1 голос
/ 02 марта 2010

Какой тип парсера вы используете?

DOM извлекает весь документ из памяти - как только вы извлекаете весь документ из памяти, ваши операции могут быть быстрыми, но в веб-приложении или в цикле forможет оказать влияние.

SAX-парсер выполняет синтаксический анализ по требованию и загружает узлы, как и когда вы запрашиваете.

Поэтому попробуйте использовать реализацию парсера, которая соответствует вашим потребностям.

0 голосов
/ 02 марта 2010

Используйте библиотеку JAXEN для xpaths: http://jaxen.codehaus.org/

...