О чем эта статья
В статье рассказывается про библиотеку 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-парсеры. С ними я еще не разбирался, но предполагаю, что там все примерно то же самое.
Список использованной литературы:
ссылка на оригинал статьи http://habrahabr.ru/post/168951/
Добавить комментарий