Boost Property Tree и его парсер XML

image

О чем эта статья

В статье рассказывается про библиотеку Property Tree Library, а именно:

  • Что такое Property Tree;
  • Примеры использования Property Tree;
  • Как конвертировать Property Tree в XML-код и обратно.

Property Tree

В Boost начиная с версии 1.41.1 появилась библиотека Property Tree. Эта библиотека предоставляет новый тип данных, древовидную структуру boost::propetry_tree::ptree.

ptree это обычная древовидная структура, каждый элемент которой, помимо данных, может содержать упорядоченный список дочерних элементов, каждый из которых имеет свое имя.

Структура выглядит так:

struct ptree {    data_type data;                         // Данные элемента    list<pair<key_type, ptree>> children;   // упорядочненный список дочерних элементов }; 

В основном в качестве key_type используется обычная строка.

Примеры использования Property Tree

Структура ptree очень удобна для чтения, записи, сохранения и загрузки древовидных данных.
Добавлять элементы в это дерево проще простого:

boost::property_tree::ptree heroTree;  heroTree.put("Name", "John"); heroTree.put("Exp", 150); heroTree.put("Inventory.Weapon", "Blue Sword"); heroTree.put("Inventory.Money", 3000); 

Поскольку ptree содержит связный список, то можно добавить несколько предметов, имеющих один ключ:

heroTree.put("Inventory.Item", "Stone"); heroTree.put("Inventory.Item", "Golden helmet"); heroTree.put("Inventory.Item", "Thomb key"); 

Желаете получить под-дерево? Тоже ничего сложного:

boost::property_tree::ptree inventoryTree = heroTree.get_child("Inventory"); inventoryTree.put("Item", "Shield of Honor"); 

Получать элементы тоже проще простого:

int exp = pt.get<int>("Exp");  std::string weapon = pt.get<std::string>("Inventory.Weapon"); 

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

int exp = pt.get<int>("Exp", 0); 

Если элементов ожидается несколько, то придется перебирать их:

BOOST_FOREACH(auto &v, inventoryTree) { 	if (v.first == "Item") 	{ 		std::cout << "Hero has item: " << v.second.get<std::string>("") << std::endl; 	} } 

XML-парсер

Самое замечательное в Property Tree Library это то, что в библиотеке содержатся встроенные парсеры XML и JSON. Парсеры не идеальные, но работают прямо «из коробки» — не нужно тянуть никаких дополнительных библиотек и зависимостей.
С JSON-парсером я не работал, но XML-парсер использую уже на полную катушку. О нем и напишу подробнее.

Для чтения xml и записи xml применяются, соответственно, методы read_xml и write_xml. Использование очень простое:

 //XML-код для парсинга std::string xmlCode = "<ButtonList>\ 	<Button>B1</Button>\ 	<Button>B2</Button>\ 	</ButtonList>";  //Создаем поток std::stringstream stream(xmlCode);  try { 	boost::property_tree::ptree propertyTree; 	 	//Читаем XML 	boost::property_tree::read_xml(stream, propertyTree); 	 	//Читаем значения: 	BOOST_FOREACH(auto &v, propertyTree) 	{ 		std::cout << "Button is " << v.second.get<std::string>("") << std::endl; 	} 	 	 	//Добавляем пару значений 	propertyTree.put("ButtonList.Button", "B3"); 	propertyTree.put("ButtonList.Button", "B4"); 	 	std::stringstream output_stream; 	 	//Записываем в другой поток 	boost::property_tree::write_xml(output_stream, propertyTree); 	 	//Получаем XML из потока 	std::string outputXmlCode = output_stream;  } catch(boost::property_tree::xml_parser_error) { 	 	std::cout<<"XML parser error!"<<std::endl; 	 	throw; } 

Если есть необходимость прочитать атрибут узла, то сделать это можно через псевдо-поддерево "<xmlattr>":

 //XML-код для парсинга std::string xmlCode = "<Data name="Position" x="5" y="5"/>";  //... парсим XML  //Получаем значения std::string name = propertyTree.get<std::string>("Data.<xmlattr>.name"); int x = propertyTree.get<std::string>("Data.<xmlattr>.x"); int y = propertyTree.get<std::string>("Data.<xmlattr>.y"); 

C псевдо-поддеревом "<xmlattr>" связаны небольшие грабли, на которые можно нечаянно наступить. Допустим, как в вышеописанном примере, вас есть элемент ButtonList в котором есть 4 элемента Button — кнопки.
Если у элемента ButtonList не указаны атрибуты, то в созданном на основе XML дереве будет 4 поддерева, как и положено, и у всех будет ключ «Button». Если же у ButtonList указаны какие-нибудь атрибуты, то — сюрприз! — к 4 поддеревьям с кнопками добавляется еще одно поддерево, с ключом "<xmlattr>", которое, очевидно, к кнопкам отношения не имеет.

Поскольку нам нужны только кнопки, значит, при переборе дочерних деревьев придется делать дополнительную проверку, которая исключит <xmlattr> из списка перебора. Например, так:

 BOOST_FOREACH(auto &v, propertyTree) { 	if (v.first == "Button") //Вводим дополнительную проверку 	{ 		std::cout << "Button is " << v.second.get<std::string>("") << std::endl; 	} }  

Прочее

Помимо XML-парсера, Property Tree Library содержит также JSON-, INI- и INFO-парсеры. С ними я еще не разбирался, но предполагаю, что там все примерно то же самое.

Список использованной литературы:

www.boost.org/doc/libs/1_52_0/doc/html/property_tree.html

ссылка на оригинал статьи http://habrahabr.ru/post/168951/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *