Должен ли я использовать XPath или просто DOM?
У меня есть куча иерархических данных, хранящихся в файле XML. Я оборачиваю это позади созданных вручную классов, используя TinyXML. Данный фрагмент XML, который описывает сигнатуру источника как набор пар (частота, уровень), выглядит примерно так:
<source>
<sig><freq>1000</freq><level>100</level><sig>
<sig><freq>1200</freq><level>110</level><sig>
</source>
Я извлекаю пары с этим:
std::vector< std::pair<double, double> > signature() const
{
std::vector< std::pair<double, double> > sig;
for (const TiXmlElement* sig_el = node()->FirstChildElement ("sig");
sig_el;
sig_el = sig_el->NextSiblingElement("sig"))
{
const double level = boost::lexical_cast<double> (sig_el->FirstChildElement("level")->GetText());
const double freq = boost::lexical_cast<double> (sig_el->FirstChildElement("freq")->GetText());
sig.push_back (std::make_pair (freq, level));
}
return sig;
}
где node() указывает на <source>
узел.
Вопрос: получу ли я более аккуратный, более изящный, более удобный для обслуживания или каким-либо иным способом более качественный кусок кода с использованием библиотеки XPath?
Обновление: я пробовал использовать TinyXPath двумя способами. Ни один из них на самом деле не работает, что, очевидно, является важным аргументом против них. Я делаю что-то в корне неправильно? Если так будет выглядеть XPath, я не думаю, что он мне что-то даст.
std::vector< std::pair<double, double> > signature2() const
{
std::vector< std::pair<double, double> > sig;
TinyXPath::xpath_processor source_proc (node(), "sig");
const unsigned n_nodes = source_proc.u_compute_xpath_node_set();
for (unsigned i = 0; i != n_nodes; ++i)
{
TiXmlNode* s = source_proc.XNp_get_xpath_node (i);
const double level = TinyXPath::xpath_processor(s, "level/text()").d_compute_xpath();
const double freq = TinyXPath::xpath_processor(s, "freq/text()").d_compute_xpath();
sig.push_back (std::make_pair (freq, level));
}
return sig;
}
std::vector< std::pair<double, double> > signature3() const
{
std::vector< std::pair<double, double> > sig;
int i = 1;
while (TiXmlNode* s = TinyXPath::xpath_processor (node(),
("sig[" + boost::lexical_cast<std::string>(i++) + "]/*").c_str()).
XNp_get_xpath_node(0))
{
const double level = TinyXPath::xpath_processor(s, "level/text()").d_compute_xpath();
const double freq = TinyXPath::xpath_processor(s, "freq/text()").d_compute_xpath();
sig.push_back (std::make_pair (freq, level));
}
return sig;
}
В качестве вторичной проблемы, если так, какую библиотеку XPath я должен использовать?
4 ответа
В целом я предпочитаю решения на базе XPath за их краткость и универсальность, но, честно говоря, в вашем случае, я не думаю, что использование XPath принесет много пользы вашему signature
,
Вот почему:
Код элегантности
Ваш код красив и компактен, и он не станет лучше с выражением XPath.
След памяти
Если ваш входной XML-файл конфигурации не является огромным (своего рода оксюморон) и анализ DOM повлечет за собой большой объем памяти, для которого нет никаких доказательств того, что использование XPath будет решающим лекарством, я бы придерживался DOM.
Скорость выполнения
В таком простом дереве XML скорость выполнения должна быть сопоставимой. Если бы была разница, это, вероятно, было бы выгодно для TinyXml из-за расположения freq
а также level
теги под данным узлом.
Библиотеки и внешние ссылки Это решающий момент.
Ведущий движок XPath в мире C++ - XQilla. Он поддерживает XQuery (следовательно, и XPath 1.0 и 2.0) и поддерживается Oracle, потому что он разработан группой, ответственной за продукты DB Berkeley (включая именно Berkeley DB XML - который использует XQilla).
Проблема для разработчиков C++, желающих использовать XQilla, состоит в том, что у них есть несколько альтернатив
- используйте Xerces 2 и XQilla 2.1, засоряйте ваш код кастами.
- используйте XQilla 2.2+ и используйте Xerces 3 (здесь не нужно кастовать)
- используйте TinyXPath, красиво интегрированный с TinyXml, но для которого, однако, существует ряд ограничений (например, нет поддержки пространств имен)
- смешайте Xerces и tinyXml
Таким образом, в вашем случае переход на XPath только ради этого принесет мало пользы, если таковой будет.
Тем не менее, XPath является очень мощным инструментом в сегодняшнем наборе инструментов для разработчиков, и никто не может его игнорировать. Если вы просто хотите попрактиковаться на простом примере, ваш так же хорош, как и любой другой. Тогда я бы помнил о вышеприведенных пунктах и, возможно, все равно использовал TinyXPath.
Вам нужен XPath, если вам нужна гибкость для внесения изменений во время выполнения в извлеченные значения.
Но если вам вряд ли понадобится такая гибкость или перекомпиляция для расширения того, что вы извлекаете, не является проблемой, и вещи меняются не часто или если пользователям никогда не нужно обновлять выражения. Или, если то, что у вас хорошо работает, вам не нужен XPath, и есть множество приложений, которые его не используют.
Что касается того, является ли это более читабельным, хорошо, да, это конечно может быть. Но если вы просто извлекаете несколько значений, я бы поставил под сомнение необходимость использовать другую библиотеку.
Я бы, безусловно, задокументировал то, что у вас есть, немного лучше, поскольку те, кто не знаком с библиотеками tinyxml или xml, могут быть не уверены в том, что они делают, но их нетрудно понять.
Я не уверен, какой тип накладных расходов добавляет XPath, но я подозреваю, что он может добавить некоторые. Для большинства, я думаю, они вообще не заметят никакой разницы, и это может не беспокоить вас или большинство людей, но имейте это в виду, если это то, что вас беспокоит.
Если вы действительно хотите использовать библиотеку xpath, то все, что я могу сказать, - это то, что я использовал ту, которая шла с Xerces-C++, и это было не слишком сложно для изучения. Я использовал TinyXML раньше, и кто-то здесь упомянул TinyXPath. У меня нет опыта с этим, но он доступен.
Я также нашел эту ссылку полезной, когда впервые узнал о выражениях XPath. http://www.w3schools.com/xpath/default.asp
XPath был создан для этого, поэтому, конечно, ваш код будет "лучше", если вы его используете.
Я не могу рекомендовать конкретную библиотеку XPath C++, но даже если ее использование будет правильным решением в большинстве случаев, прежде чем добавлять ее, проведите анализ затрат и выгод. Может быть, ЯГНИ.
Это выражение XPath:
/*/sig[$pN]/*
выбирает все дочерние элементы (только пара freq
а также level
) из $pN-го sig
дочерний элемент верхнего элемента XML-документа.
Строка $pN
должен быть заменен определенным положительным целым числом, например:
/*/sig[2]/*
выбирает эти два элемента:
<freq>1200</freq><level>110</level>
Использование выражения XPath, поскольку это, очевидно, намного короче и понятнее, чем предоставляемый код C++.
Другое преимущество заключается в том, что одно и то же выражение XPath можно использовать из программ на C#, Java или... без какой-либо его модификации - таким образом, соблюдение XPath приводит к очень высокой степени переносимости.