Форум программистов, компьютерный форум, киберфорум
Наши страницы
C++
Войти
Регистрация
Восстановить пароль
 
Рейтинг 4.74/27: Рейтинг темы: голосов - 27, средняя оценка - 4.74
ForEveR
В астрале
Эксперт С++
7995 / 4754 / 651
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
#1

SAX json парсер

05.07.2013, 22:46. Просмотров 4825. Ответов 6
Метки нет (Все метки)

Вечер добрый.
Есть задача реализовать SAX-парсер JSON-a (парсинг не во временные переменные, а сразу в текущие, на которые указатели/ссылки сохранены в некий контекст в классе). Задача парсить не просто примитивные типы и контейнеры, но и сложные/составные типы.

Нечто вроде такого

C++
1
2
3
4
5
6
struct A
{
   int a;
   std::vector<int> b;
   std::string c;
};
Должно переводиться в json и обратно
C++
1
2
3
4
5
const std::string json = "{\"A\":{\"a\":1,\"b\":{1,2},\"c\":\"string\"}}";
A a;
statement stmt;
stmt.use(a);
stmt.deserialize(json);
Я сделал данный кейс через fusion (сделано по аналогу SOCI, но там нет парсинга СРАЗУ в сложные типы). Капитально приходится испытывать сложности с составными объектами, я выбрал вариант, когда просто биндятся simple/container/conversion типы, который находятся в структуре (страдает время компиляции, для каждого объекта приходится создавать новый statement, причем динамически, ибо хранятся указатели (это проще, постольку поскольку, конструктор копирования реализовывать несколько сложнее), уходит довольно много памяти (допустим для примера из сериализации/десериализации двух структур + массива интов + boost::gregorian::date уходит примерно 271к байт, терпимо, но не слишком круто)).
Пример:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
struct other
{
   std::string name;
   unsigned int age;
   bool flag;
   double number;
 
   struct name_key { static const char* name() { return "name"; }};
   struct age_key { static const char* name() { return "age"; }};
   struct flag_key { static const char* name() { return "flag"; }};
   struct number_key { static const char* name() { return "number"; }};
};
 
BOOST_FUSION_ADAPT_ASSOC_STRUCT(
      other,
      (std::string, name, other::name_key)
      (unsigned int, age, other::age_key)
      (bool, flag, other::flag_key)
      (double, number, other::number_key)
);
структура становится fusion_sequence и через код вроде этого ее можно обойти

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
template<typename Next, typename Last>
struct traverse_types
{
   template<typename Object>
   static void traverse_helper(statement& stmt, Object& object, const std::string& name)
   {
      typedef typename get_exchange_type<Object>::type type;
      stmt.add_field(name, new type(object, name));
   }
   template<typename Object>
   static void apply(statement& stmt, Object& object)
   {
      typedef typename boost::fusion::result_of::key_of<Next>::type key_t;
      typedef typename boost::fusion::result_of::next<Next>::type next_t;
      typedef typename boost::fusion::result_of::at_key<Object, key_t>::type value_ref_t;
      typedef typename boost::remove_reference<value_ref_t>::type value_t;
      traverse_helper(stmt, boost::fusion::at_key<key_t>(object), key_t::name());
      traverse_types<next_t, Last>::apply(stmt, object);
   }
};
 
template<typename Last>
struct traverse_types<Last, Last>
{
   template<typename Object>
   static void apply(statement&, Object&)
   {
   }
};
// в конкретном классе use/into
 static void traverse(statement& stmt, T& object)
 {
    traverse_types<typename boost::fusion::result_of::begin<T>::type,
      typename boost::fusion::result_of::end<T>::type>::
      apply(stmt, object);
};
Где get_exchange_type некий шаблон, который просто typedef-ит нужный into/use тип.

C++
1
2
3
4
5
6
7
8
9
template<typename T>
struct get_exchange_type
{
   typedef typename exchange_traits<T>::type_tag tag;
   typedef typename boost::mpl::if_<typename boost::is_same<tag, basic_tag>::type, into_type<T>,
           typename boost::mpl::if_<typename boost::is_same<tag, container_tag>::type, container_into_type<T>,
           typename boost::mpl::if_<typename boost::is_same<tag, conversion_tag>::type, conversion_into_type<T>,
           user_into_type<T>>::type>::type>::type type;
};
C++
1
2
3
4
5
6
7
8
9
template<typename T>
struct get_exchange_type<const T>
{
   typedef typename exchange_traits<T>::type_tag tag;
   typedef typename boost::mpl::if_<typename boost::is_same<tag, basic_tag>::type, use_type<T>,
           typename boost::mpl::if_<typename boost::is_same<tag, container_tag>::type, container_use_type<T>,
           typename boost::mpl::if_<typename boost::is_same<tag, conversion_tag>::type, conversion_use_type<T>,
           user_use_type<T>>::type>::type>::type type;
};
Перегрузка для statement/writer реализована на тегах
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
template<typename T>
struct type_conversion;
 
struct basic_tag {};
struct user_tag {};
struct container_tag {};
struct conversion_tag {};
 
template<typename T>
struct has_type_conversion
{
   typedef char no;
   typedef char yes[2];
 
   template<typename U>
   static yes& test(typename type_conversion<U>::base_type*);
   template<typename>
   static no& test(...);
 
   static const bool value = sizeof(test<T>(0)) == sizeof(yes);
   typedef boost::mpl::bool_<value> type;
};
 
template<typename T>
struct exchange_traits
{
   typedef typename has_type_conversion<T>::type conversion;
   typedef typename boost::mpl::if_<conversion, conversion_tag, user_tag>::type type_tag;
   template<typename U, typename = void>
   struct get_type
   {
      enum { x_type = x_object };
   };
   template<typename U>
   struct get_type<U, typename boost::enable_if<typename has_type_conversion<U>::type>::type>
   {
      enum { x_type = exchange_traits<typename type_conversion<U>::base_type>::x_type };
   };
   enum
   {
      x_type = get_type<T>::x_type
   };
};
Дальше идут специализации для basic типов / контейнеров.

Есть возможность сделать интроспекцию/отражение в С++ проще, чем сделано на данный момент? Если данный вопрос нужно перенести в С++ для экспертов/boost - скажите, перенесу. Спасибо.
5
Лучшие ответы (1)
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.07.2013, 22:46
Ответы с готовыми решениями:

Парсер на С
вопшем есть файл с текстом..... в етом файле есть какие даные(мусор)...и...

Парсер выражений
Здравствуйте, помогите пожалуйста написать простой парсер, для дальнейшей...

Парсер
Для небольшой текстовой игрушки в жанре RPG нужен способ считывания данных из...

Парсер IP пакета
Здравствуйте. Необходима помощь. Имеется программа которая с помощью функции...

Некий парсер
Допустим есть строка: 1 hour ago &lt;span style=&quot;color:...

6
Nixy
ComfyMobile
400 / 281 / 34
Регистрация: 24.07.2012
Сообщений: 916
05.07.2013, 23:37 #2
Вам наверное стоит все-таки тему у экспертов размещать, врятли в разделе новичков ответят.
1
gray_fox
05.07.2013, 23:55
  #3

Не по теме:

Цитата Сообщение от ForEveR Посмотреть сообщение
реализовать SAX-парсер JSON-a (парсинг не во временные переменные, а сразу в текущие, на которые указатели/ссылки сохранены в некий контекст в классе)
Так, между делом: готовых решений для С++ действительно нету?

0
ForEveR
В астрале
Эксперт С++
7995 / 4754 / 651
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
05.07.2013, 23:56  [ТС] #4
gray_fox, Не видел. Искал. Выбрали RapidJson за то, что он позволяет использовать SAX парсинг + быстрый, но вот распих в переменные приходится делать самому.
По хорошему готовых решений-то и не найти, даже тот же SOCI, который инкапсулирует в себе взаимодействие практически со всеми СУБД не умеет работать с пользовательскими объектами напрямую, ибо без некой реализации интроспекции это сделать невозможно. SOCI работает +- так
C++
1
2
3
4
5
6
7
8
9
template<>
struct conversion_traits<compound_type>
{
   typedef values base_type;
   void from_base(const base_type& in, indicator& ind, compound_type& out)
   {
       out.field = in.get<type>("field");
   }
};
Что неприемлимо в моем случае.
1
fasked
Эксперт С++
4981 / 2560 / 241
Регистрация: 07.10.2009
Сообщений: 4,311
Записей в блоге: 1
15.07.2013, 09:02 #5
Лучший ответ Сообщение было отмечено как решение

Решение

Можно взглянуть на библиотеку cereal, правда в глубине там тот же rapidjson. Подробностей, к сожалению, не знаю и прошу прощения, если совсем не в тему, но на первый взгляд весьма похоже на то, что требуется.
3
ForEveR
В астрале
Эксперт С++
7995 / 4754 / 651
Регистрация: 24.06.2010
Сообщений: 10,547
Завершенные тесты: 3
16.07.2013, 12:26  [ТС] #6
fasked, Интересно. Посмотрел. Но... Есть множество вещей, которые меня не устраивают на данном этапе (как минимум использование С++11, хотя с этим еще как-то можно смириться), а вот то что имена в объекте json-а должны идти в том же порядке, как указано в serialize - это очень и очень грустно.
0
fasked
Эксперт С++
4981 / 2560 / 241
Регистрация: 07.10.2009
Сообщений: 4,311
Записей в блоге: 1
16.07.2013, 21:46 #7
Цитата Сообщение от ForEveR Посмотреть сообщение
а вот то что имена в объекте json-а должны идти в том же порядке, как указано в serialize - это очень и очень грустно
В документации об этом сказано "At this time", так что можно надеяться на исправление ситуации.
0
16.07.2013, 21:46
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.07.2013, 21:46

Парсер HTLM
Можно ли спарсить страницу что одни данные пошли в ComboBox (Имя пользователя)...

Парсер HTML
Здравствуйте! Нигде не могу найти парсера HTML на Си/С++, помогите кто чем...

Парсер сайтов
Доброго времени суток. У меня возник вопрос. Делаю парсер, вынимает из сайта то...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
7
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru