Как повысить производительность запроса XML-файла с помощью VTD-XML и XPath?

#java #xml #vtd-xml

#java #xml #vtd-xml

Вопрос:

Я запрашиваю XML-файлы размером около 1 МБ (более 20 тыс. строк). Я использую XPath для описания того, что я хочу получить, и библиотеку VTD-XML для ее получения. Я думаю, что у меня некоторые проблемы с производительностью.

Проблема в том, что я делаю около 5 тыс. запросов к XML-файлу. Для извлечения всех значений требуется примерно 16-17 секунд. Я хочу спросить вас, является ли это нормальной производительностью для такой задачи? Как я могу это улучшить?

Я использую библиотеку VTD-XML с автопилотом, который дает мне возможность использовать XPath. Реализация выглядит следующим образом:

 private VTDGen vg = new VTDGen();
private VTDNav vn;
private AutoPilot ap = new AutoPilot();

public void init(String xml) {
    log.info("Creating document");
    xml = xml.replace("<?xml version="1.0"?>", "<?xml version="1.0" encoding="UTF-8"?>");
    byte[] bytes = xml.getBytes(StandardCharsets.UTF_8);
    vg.setDoc(bytes);
    try {
        vg.parse(true);
        vn = vg.getNav();
    } catch (ParseException e) {
        e.printStackTrace();
    }
    log.info("Document created");
}

public String parseXmlOrReturnNull(String query) {
    String xPathStringVal = null;
    try {
        ap.selectXPath(query);
        ap.bind(vn);
        int i = -1;
        while ((i = ap.evalXPath()) != -1) {
            xPathStringVal = vn.getXPathStringVal();
        }
    }catch (XPathEvalException e) {
        e.printStackTrace();
    } catch (NavException e) {
        e.printStackTrace();
    } catch (XPathParseException e) {
        e.printStackTrace();
    }
    return xPathStringVal;
}
  

Мои XML-файлы имеют определенный формат, они разделены на множество частей — сегментов, и мои запросы одинаковы для всех сегментов (я запрашиваю его в цикле). Например, часть xml:

 <segment>
    <a>
        <b>value1</b>
        <c>
            <d>value2</d>
            <e>value3</d>
        </c>
    </a>
</segment>
<segment>
    <a>
        <b>value4</b>
        <c>
            <d>value5</d>
            <e>value6</d>
            <f>value6</d>
        </c>
    </a>
</segment>
...
  

Если я хочу получить значение 1 в первом сегменте, я использую запрос:

 //segment[1]/a/b
  

для значения 4 во втором сегменте

 //segment[2]/a/b
  

и т.д.

Интуиция говорит несколько вещей: в моем подходе каждый запрос независим (он ничего не знает о другом запросе), это означает, что АвтоПилот, мой итератор, всегда начинается с начала файла, когда я хочу его запросить.

Мой вопрос: есть ли какой-нибудь способ установить автопилот в начале сегмента обработки? И когда я закончу запрос, переместите автопилот в следующий сегмент? Я думаю, что если мой метод начнет поиск значения не с начала, а с точки указания, это будет намного быстрее.

Другой способ — разделить xml-файл на небольшие XML-файлы (один XML-файл = один сегмент) и запрашивать эти небольшие XML-файлы.

Что вы думаете, ребята? Заранее спасибо

Комментарии:

1. Любой шаблон, начинающийся с //x , невероятно дорогостоящий, это означает «проверить наличие узла с именем x на любой глубине», что в основном заставляет ваш процессор извлекать каждый узел, чтобы просмотреть их. Если вы знаете , какие segment файлы представляют для вас интерес, укажите свой путь, например /segment[1] , или /whatever/segment[1] будет значительно быстрее. Второе: если ваши запросы XPath известны заранее, предварительно скомпилируйте их (я недостаточно разбираюсь в VTD XML, но это стандартная функция большинства движков XPath).

Ответ №1:

Незначительный: Замена не требуется, поскольку UTF-8 является кодировкой по умолчанию; только при наличии кодировки нужно будет исправить ее на UTF-8.

XPath должен выполняться только один раз, чтобы не начинаться с [0] до следующего индекса.

Если вам нужно представление списка, вы можете использовать JAXB с аннотациями.

Вероятно, лучше всего использовать примитивный синтаксический анализ на основе событий без объекта DOM (SAXParser).

 Handler handler = new org.xml.sax.helpers.DefaultHandler {
    @Override
    public void startElement(String uri, 
        String localName, String qName, Attributes attributes) throws SAXException {
    }

    @Override
    public void endElement(String uri, 
        String localName, String qName) throws SAXException {
    }

    @Override
    public void characters(char ch[], int start, int length) throws SAXException {
    }
};
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser parser = factory.newSAXParser();
InputStream in = new ByteArrayInputStream(bytes);
parser.parse(in, handler);