C++: разбор строки JSON с ключами, не заключенными в двойные кавычки

Я успешно использую библиотеку Casablanca Json C++ (cpprest) в течение некоторого времени. Его синтаксический анализатор (web::json::value::parse(<json_string>)) отлично работает с действительными строками JSON. Скажите, что это будет правильно проанализировано:

{
  "key1": [["1", 0.4], ["0", 0.6]],
  "key2": true,
  "key3": 1,
  "key4": [{"key41": 1}, {"key42": [1,2,3]}]
}    

Теперь столкнулся с необходимостью разбора JSON-объектов, ключи которых не заключены в двойные кавычки:

{
  key1: [[1, 0.4], [0, 0.6]],
  key2: true,
  key3: 1,
  key4: [{key41: 1}, {key42: [1,2,3]}]
}

Есть ли хороший способ правильно проанализировать это, а затем сериализовать в действительный JSON, чтобы Casablanca могла правильно проанализировать результирующий действительный JSON?

Hjson, похоже, подходит для этой цели, но не предоставляет необходимой библиотеки для C++. Они упоминают библиотеку jzon для C - я пробовал: у нее только односторонний синтаксический анализ (без сериализации), и даже синтаксический анализ работает неправильно (даже не может анализировать действительные JSON)


person Oleg Shirokikh    schedule 08.10.2016    source источник
comment
Итак, вы просите, чтобы сторонний ресурс был способен на это, зная, что такие вопросы здесь не по теме?   -  person πάντα ῥεῖ    schedule 08.10.2016
comment
@πάνταῥεῖ Он спрашивает о преобразовании в none? Это то, что я понимаю. Хорошо, я начну отвечать   -  person amanuel2    schedule 08.10.2016
comment
Вы, случайно, не контролируете то, что генерирует это? Чтобы вы могли заставить его генерировать действительный JSON?   -  person little pootis    schedule 08.10.2016
comment
@ πάνταῥεῖ нет, я этого не говорил .. любое хорошее решение для синтаксического анализа предварительной обработки C ++ тоже будет работать нормально. но если кто-то знает стороннюю библиотеку, почему бы и нет?   -  person Oleg Shirokikh    schedule 08.10.2016
comment
@littlepootis нет, если бы я хотел - у меня бы вообще не было этой проблемы   -  person Oleg Shirokikh    schedule 08.10.2016


Ответы (3)


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

У вас есть объект, похожий на javascript. Давайте подключим его к движку javascript и воспользуемся им, чтобы выдать правильный JSON. Я буду использовать Qt QJSEngine, так как я с ним хорошо знаком:

constexpr char const* str = R"({
    key1: [[1, 0.4], [0, 0.6]],
    key2: true,
    key3: 1,
    key4: [{key41: 1}, {key42: [1,2,3]}]
})";

QJSEngine e;

QString script = QString("JSON.stringify(%0)").arg(str);

то вы можете просто оценить это:

e.evaluate(script).toString().toStdString()

урожаи

{"key1":[[1,0.4],[0,0.6]],"key2":true,"key3":1,"key4":[{"key41":1},{"key42":[1,2,3]}]}
person krzaq    schedule 08.10.2016
comment
Благодарность! хм, интересный подход. скоро попробую - person Oleg Shirokikh; 08.10.2016

Этот грубый метод будет работать.

(Непроверенный код)

У нас есть три состояния: NEUTRAL, ONSTRING и ONALNUM.

Начинаем с НЕЙТРАЛЬНОГО. Если мы нажмем '"', мы перейдем в ONSTRING. Если мы нажмем альфу, мы перейдем в ONALNUM. Если мы войдем или выйдем из alnum, мы выводим кавычки. Мы также выводим прочитанный символ. Если мы находимся в ONALNUM, мы выходим из этого, когда мы нажимаем non-alnum, amd переходит в NEUTRAL, если мы не нажимаем кавычки, когда это ошибка синтаксического анализа.Если мы находимся в ONSTRING, мы применяем правила экранирования строки JSON, которые я не знаю навскидку.

 #define NEUTRAL 0
 #define ONSTRING 1
 #define ONALNUM 2

 int state = NEUTRAL;
 char *inptr = str;
 char ch;

 while( (ch = *inptr++))
 {
    if(state == NEUTRAL)
    {
       if( isalpha(ch) )
       {
           emit('\"');
           state = ONALNUM;
       }
       else if(ch = '\"')
          state = ONSTRING;
       emit(ch);
    }
    else if(state == ONSTRING)
    {
        /* JSON string escape rules here */
        if(ch == '\"')
          state = NEUTRAL;
        emit(ch);
    }
    else if(state == ONALNUM)
    {
       emit(ch);
       if(!isalnum(ch))
       {
          state = NEUTRAL;
          emit('\"');
       }
    }
 }
person Malcolm McLean    schedule 08.10.2016

Проблема

Судя по контексту вашего вопроса, вы хотите удалить «», чтобы иметь правильный формат JSON.

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

Решение

Для этого мы воспользуемся функцией std::replace из библиотеки <algorithm>; хотя мы можем реализовать это самостоятельно, лучше использовать стандартные библиотеки, так как их создатели много работали над оптимизацией этих функций до самых полных возможностей. Итак, давайте возьмем ваш код, который вы нам дали из вопроса, и сделаем его подходящим для JSON.

#include <algorithm>
#include <string>
#include <iostream>

using std::string;
using std::cout;
using std::endl;

void convert_char(string &s,char from_conv, char to_conv) {
  std::replace( s.begin(), s.end(), from_conv, to_conv); // replace all 'x' to 'y'
}

int main()
{
    string str =  "{ \n \
    \"key1\": [[\"1\", 0.4], [\"0\", 0.6]], \n \
    \"key2\": true, \n \
    \"key3\": 1,  \n \
    \"key4\": [{\"key41\": 1}, {\"key42\": [1,2,3]}] \n }";;
    convert_char(str,'\"',(char)0);
    cout << str << endl;
}

Как видите, у нас есть функция convert_char, которая преобразует один символ в другой. Итак, в основном, как вы задали вопрос, мы удалили двойную кавычку, и тада, она отформатирована как JSON! Посмотрите здесь демоверсию.

Решение для парсера JSON

Очевидно, здесь вы будете использовать библиотеку, которая сделает это за вас. Я собираюсь представить вам sciter! По сути, с sciter все, что вам нужно сделать, это:

#include <algorithm>
#include <string>
#include <iostream>
#include <sciter>

using std::string;
using std::cout;
using std::endl;

int main()
{
    string str =  "{ \n \
    \"key1\": [[\"1\", 0.4], [\"0\", 0.6]], \n \
    \"key2\": true, \n \
    \"key3\": 1,  \n \
    \"key4\": [{\"key41\": 1}, {\"key42\": [1,2,3]}] \n }";;
    sciter::value str_conv = sciter::value::from_string( str, CVT_JSON_LITERAL );
    cout << str_conv << endl;
}

Теперь, согласно этому коду, код в формате JSON находится в str_conv! Ссылки, которые помогут вам в этом, находятся ниже в разделе «Ссылки».

Использованная литература:

sciter

cpprefrence std::replace

string::replace

cpprefrence std::refrence_if

Глоссарий

std::replace :

Прототип:

template <class ForwardIterator, class T>
  void replace (ForwardIterator first, ForwardIterator last,
                const T& old_value, const T& new_value); //source cpprefrence

ссылки: cpprefrence

<algorithm>:

Многие темы находятся внутри библиотеки алгоритмов. Это библиотека для, как вы уже догадались, алгоритмов.

Заголовок <algorithm> определяет набор функций, специально разработанных для использования с диапазонами элементов.

Диапазон — это любая последовательность объектов, к которым можно получить доступ через итераторы или указатели, например массив или экземпляр некоторых контейнеров STL. Заметьте, однако, что algorithms работает через iterators непосредственно со значениями, не влияя никоим образом на структуру любого возможного контейнера (это никогда не влияет на размер или распределение памяти контейнера).

Библиотека алгоритмов определяет функции для различных целей (например, поиска, сортировки, подсчета, манипулирования), которые работают с диапазонами элементов.

Использованная литература:

cplusplus

cppreference

person amanuel2    schedule 08.10.2016
comment
В объезд. JSON настаивает на заключении в кавычки идентификаторов и строк. - person Malcolm McLean; 08.10.2016
comment
Даже если бы это было то, чего хотел ОП, правильным способом было бы использовать идиому стирания-удаления, а не оставлять строку, полную '\0' символов. - person krzaq; 08.10.2016
comment
@MalcolmMcLean Это было исправлено. Посмотрите на новый ответ ;) - person amanuel2; 08.10.2016
comment
спасибо, но я не думаю, что это поможет. Что касается второй части (анализ) - у меня нет проблем - я упомянул, что широко использую библиотеку Casablanca для JSON - без проблем. Первая часть (замена ) тоже не будет работать, потому что внутри пар JSON все еще есть двойные кавычки, окружающие ЗНАЧЕНИЯ, а не только ключи. - person Oleg Shirokikh; 08.10.2016
comment
@amanuel2 replace делает именно это - заменяет элементы. Вы помещаете нулевой терминатор (char)0 - C во все места, где раньше был ". Попробуйте передать str_conv.c_str() в cout и посмотрите, что происходит. - person krzaq; 08.10.2016
comment
@krzaq ааа!!!! Спасибо большое! Вот почему у меня было так много нулевых терминаторов в других моих кодах C++! Хорошо, я должен сделать ' '... еще раз извините :-( - person amanuel2; 08.10.2016