Это просто не передать словами… Когда приходится писать код вида:
std::string get_service_name( const Document & document )
{
AutoPtr< NodeList > services = document.getElementsByTagName( "service" );
if( 1 != services->length() )
throw ...;
Node * first = services->item( 0 )->firstChild();
if( !first )
throw ...;
if( Node::TEXT_NODE != first->nodeType() )
throw ...;
return first->nodeValue();
}
я испытываю если не рвотные позывы, то что-то очень близкое к ним.
Хочется попробовать в деле что-то вроде Code Synthesis’s C++/Tree, да нет времени, трясти нужно. Если у кого-то есть опыт работы с данным продуктом, поделитесь, пожалуйста, впечатлениями – как оно? Еще интересно, сколько стоит коммерческая лицензия, а то они на сайте цену не указывают.
Ну если сокращать конкретно этот код (не обращая внимания на то, что при парсинге XML/YAML обычными С++-методами такой код вообще почти везде :-)), то можно последние строчки выделить в отдельную функцию std::string xml::get_single_child_value(Node*) и мы сэкономим 5 строчек!
ОтветитьУдалитьСделаем ещё одну функцию, чтобы сократить начало.
Node* xml::get_document_single_node_by_name(Document*, const std::string&)
Итого одна длинная строчка!
Чем не костыль? :-)
P.S. Я с Вами полностью согласен, да. Ненавижу.
Ну да, как только такой код начал плодиться, я почти сразу выделил такие функции.
ОтветитьУдалитьНасколько я понимаю (оставив в стороне комментарии про XPath и прочее в том же духе), уродцем этот код делает вовсе не XML, а параноидальные проверки валидности на каждом шагу.
ОтветитьУдалитьПотому что без них он бы превратился в
document.getElementsByTagName( "service" )->item( 0 )->firstChild()->nodeValue();
...что является почти что прямым изложением соответствующего XPath-выражения.
XML-то причём? Если проверять каждый элемент сложных данных с таким же уровнем тщательности, то код всегда будет так выглядеть.
Может попробовать Poco::Util::XMLConfiguration ?
ОтветитьУдалитьТипа так:
struct service_xml_t : public Poco::Util::XMLConfiguration
{
service_xml_t( const std::string& file ) : XMLConfiguration( file ) {}
std::string get_service_name() const
{
std::string value;
if ( getRaw( "service.name[0]", value ) )
return value;
throw ...
}
};
...
service_xml_t service( "1.xml" );
std::cout << service.get_service_name() << std::endl;
Получается что-то вроде XPath. Они даже сами об этом пишут:
http://www.appinf.com/docs/poco/Poco.Util.XMLConfiguration.html
Роман
2coolaga: за подсказку спасибо, раньше я про XMLConfiguration не знал. Посмотрю поближе.
ОтветитьУдалить2zverok: если параноидальная проверка должна присутствовать (а она должна, поскольку данные приходят от партнера), то код по работе с XML вырождается именно в такие штуки. И даже XPath, имхо, здесь не будет большим помошником.
ОтветитьУдалитьА XML здесь причем, поскольку это угробищный формат и инструменты для него угробищные.
Насчёт параноидности...
ОтветитьУдалитьА несложно её в выражение завернуть:
template<class T> bool exists(T*p)
{ return p!=0; }
template<class T> bool singular(T*p)
{ return p!=0 && p->length()==1; }
template<class T> bool textnode(T*p)
{ return p!=0 && p->nodeType()==TEXT_NODE; }
template<class T> T* ensure(bool(*test)(T*), T* p)
{
if(!test(p)) throw .....;
return p;
}
.....
return
ensure(textnode,
ensure(exist,
ensure(singular, document.getElementsByTagName("service")
)->item(0)
)->firstChild()
)->nodeValue();
Или, в инфиксной форме, - нужно пойти на маленькие хитрости: перегрузить оператор, ну скажем, ->*
document.getElementsByTagName("service") ->* ensure(exists) ->* ensure(singular) -> item(0) ->* ensure(exists) ->* firstChild() ->* ensure(textnode) -> nodeValue();
Но это уже совсем уличная магия будет. Пошаговой отладке практически недоступная :)))
Проклятые рудники!
ОтветитьУдалитьПочему нет тэга <pre>
2Kodt: такой магический код я бы своих подчиненных заставил бы переписывать ;)
ОтветитьУдалить