О чем эта статья
В статье рассказывается про библиотеку 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 поддерева с кнопками, как и положено. Если же у ButtonList указаны какие-нибудь атрибуты, то — сюрприз! — к 4 поддеревьям с кнопками добавляется еще одно поддерево, с ключом "<xmlattr>", которое, очевидно, к кнопкам отношения не имеет.
Если мы будем перебирать прямо все поддеревья, то вместе с кнопками мы попробуем обработать "<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
Автор: Mephi1984