Мы сегодня обновили свою небольшую библиотеку json_dto, которая служит хоть и тонкой, но очень полезной оберткой над такой замечательной штукой, как rapidjson. Мы сделали json_dto где-то года полтора назад, взяв идеи из Boost.Serialization, для уменьшения объема писанины при работе с JSON в C++. Например, json_dto позволяет писать вот так:
class User { public : /* ... */ template<typename JSON_IO> void json_io(JSON_IO & io) { io & json_dto::mandatory("id", _id) & json_dto::mandatory("name", _name) & json_dto::mandatory("birthday", _birthday) & json_dto::mandatory("phone", _phone); } }; |
вместо того, чтобы писать вот такие простыни (взято отсюда как пример несложного кода):
class User { public: /* ... */ rapidjson::Document toJSON() { rapidjson::Value json_val; rapidjson::Document doc; auto& allocator = doc.GetAllocator(); doc.SetObject(); json_val.SetUint64(_id); doc.AddMember("id", json_val, allocator); json_val.SetString(_name.c_str(), allocator); doc.AddMember("name", json_val, allocator); // see http://rapidjson.org/md_doc_tutorial.html#DeepCopyValue json_val.CopyFrom(_birthday.toJSON(), allocator); doc.AddMember("birthday", json_val, allocator); json_val.SetUint64(_phone); doc.AddMember("phone", json_val, allocator); return doc; } static User fromJSON(const rapidjson::Value& doc) { if(!doc.IsObject()) throw std::runtime_error("document should be an object"); static const char* members[] = { "id", "name", "phone", "birthday" }; for(size_t i = 0; i < sizeof(members)/sizeof(members[0]); i++) if(!doc.HasMember(members[i])) throw std::runtime_error("missing fields"); uint64_t id = doc["id"].GetUint64(); std::string name = doc["name"].GetString(); uint64_t phone = doc["phone"].GetUint64(); Date birthday = Date::fromJSON(doc["birthday"]); User result(id, name, phone, birthday); return result; } /* ... */ }; |
Версия 0.2 нарушает совместимость с версий 0.1, хотя с этим могут столкнуться только те, кому приходилось писать собственные специализации шаблонной функции json_dto::read_json_value.
Основное изменение в json_dto-0.2 -- это добавление еще одного способа поддержки ввода-вывода пользовательских типов. В версии 0.1 сериализовать/десериализовать собственный тип можно было только одним способом: сделать собственную специализацию шаблонной функции read_json_value из пространства имен json_dto. К сожалению, это не работало для случаев, когда нужно было сделать свою сериализацию для своего шаблонного типа.
В json_dto-0.2 теперь можно определить свои собственные версии read_json_value и write_json_value в том же самом пространстве имен, в котором определен и сериализуемый тип. Нужные read_json_value/write_json_value будут найдены компилятором автоматически за счет ADL (argument dependent lookup).
Вот пример того, как теперь определяется кастомная сериализация/десериализация для собственного шаблонного типа:
// Наше собственное пространство имен, в котором определяется наш шаблонный тип. namespace bounded_types { // Шаблонный тип. // Экземпляры объектов этого типа нужно будет уметь сериализовать/десериализовать. template< typename T, T min, T max, T default_value = T{} > class bounded_value_t { public : using value_type = T; bounded_value_t() { ensure_valid( m_value ); } bounded_value_t( value_type value ) : m_value{ ensure_valid( value ) } {} value_type get() const { return m_value; } void set( value_type v ) { m_value = ensure_valid( v ); } private : value_type m_value{ default_value }; static value_type ensure_valid( value_type v ) { if( v < min || v > max ) throw std::invalid_argument{ "value is out of range!" }; return v; } }; // Определяем функцию десериализации шаблона bounded_value_t. template< typename T, T min, T max, T default_value > void read_json_value( bounded_value_t< T, min, max, default_value > & value, const rapidjson::Value & from ) { using json_dto::read_json_value; T v{ default_value }; read_json_value( v, from ); value.set( v ); } // Определяем функцию сериализации шаблона bounded_value_t. template< typename T, T min, T max, T default_value > void write_json_value( const bounded_value_t< T, min, max, default_value > & value, rapidjson::Value & object, rapidjson::MemoryPoolAllocator<> & allocator ) { using json_dto::write_json_value; write_json_value( value.get(), object, allocator ); } } /* namespace bounded_types */ |
Ну а далее все используется уже привычным образом:
struct message_t { // Вот в таком виде наш шаблон будет использоваться. using priority_t = bounded_types::bounded_value_t< int, 0, 9, 5 >; message_t() = default; message_t( std::string from, std::tm when, std::string text, priority_t::value_type priority, importance_levels::level_t importance ) {...} std::string m_from; std::tm m_when; std::string m_text; priority_t m_priority; importance_levels::level_t m_importance; template < typename JSON_IO > void json_io( JSON_IO & io ) { io & json_dto::mandatory( "from", m_from ) & json_dto::mandatory( "when", m_when ) & json_dto::mandatory( "text", m_text ) & json_dto::mandatory( "priority", m_priority ) & json_dto::optional( "importance", m_importance, importance_levels::normal ); } }; |
Старый способ с собственной специализацией read_json_value/write_json_value в пространстве имен json_dto продолжает работать. Но нужно обратить внимание, что теперь у read_json_value другой формат:
template < typename DTO > void read_json_value( DTO & v, const rapidjson::Value & object ); |
Т.е. первым параметром теперь является ссылка на объект, подлежащий десериализации.
Формат write_json_value остался прежним.
Еще в json_dto-0.2 появилась поддержка std::experimental::optional и std::optional. Так что если ваш компилятор поддерживает какой-то из этих типов, то можно использовать их совместно с json_dto без каких-либо дополнительных усилий.
В общем, нам json_dto экономит много сил и времени при работе с JSON-ом. Приглашаем взять и попробовать. Библиотека распространяется под 3-х пунктной BSD-лицензией, т.е. безвозмездно, т.е. даром ;) Если появятся какие-то соображения или предложения по развитию и расширению json_dto, то мы с удовольствием их выслушаем.
Комментариев нет:
Отправить комментарий