#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);