C++: анализ строки JSON с ключами, не заключенными в двойные кавычки
В течение некоторого времени я успешно использую библиотеку Cabablanca 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)
3 ответа
Вероятно, это не будет самым быстрым способом сделать это, но если милость измеряется в наименьших строках кода, она будет довольно высокой.
То, что у вас есть, это javascript-подобный объект. Давайте подключим его к движку javascript и используем его, чтобы выплевывать правильный JSON. Я буду использовать Qt's 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]}]}
Этот грубый метод будет работать.
(Непроверенный код)
У нас есть три состояния, НЕЙТРАЛЬНЫЕ, ONSTRING и ONALNUM.
Мы начинаем в НЕЙТРАЛЬНО. Если мы нажимаем "", мы переходим в ONSTRING. Если мы нажимаем альфа, мы переходим в ONALNUM. Если мы входим или выходим из alnum, мы выдаем кавычку. Мы также испускаем прочитанный символ. Если мы в ONALNUM, мы выходим когда мы нажимаем не alnum, amd переходит в НЕЙТРАЛЬНО, если только мы не нажимаем кавычку, когда это ошибка синтаксического анализа. Если мы находимся в 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('\"');
}
}
}
проблема
В контексте вашего вопроса вам кажется, что вы хотите удалить "", чтобы иметь правильный формат 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 Parser
Очевидно, что здесь вы будете использовать библиотеку, чтобы сделать это для вас. Я собираюсь представить 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
! Ссылки, которые помогут вам в этом, приведены ниже в разделе "Ссылки".
Рекомендации:
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
непосредственно на значения, никак не влияющие на структуру любого возможного контейнера (это никогда не влияет на размер или распределение хранилища контейнера).Библиотека алгоритмов определяет функции для различных целей (например, поиск, сортировка, подсчет, манипулирование), которые работают с диапазонами элементов.
Рекомендации: