Простой вывод строки JSON для С++?
У меня есть очень простая программа, которая выводит простую строку JSON, которую я вручную объединяю и выводю через поток std:: cout (выход действительно такой простой), но у меня есть строки, которые могут содержать двойные кавычки, фигурные -браски и другие символы, которые могут сломать строку JSON. Поэтому мне нужна библиотека (или функция более точно), чтобы избежать строк в соответствии со стандартом JSON, насколько это возможно, не более, не что иное.
Я нашел несколько библиотек, которые используются для кодирования целых объектов в JSON, но имея в виду, что моя программа - 900 строк cpp файла, я скорее не хочу полагаться на библиотеку, которая в несколько раз больше, чем моя программа, просто для достижения чего-то так же просто, как это.
Ответы
Ответ 1
Обновить. Не используйте это! vog предоставляет гораздо более полное (и в равной степени компактное) решение ниже: fooobar.com/questions/101757/...
Здесь очень простой запуск, однако он не обрабатывает недопустимые символы Unicode. Если вы не ожидаете какого-либо из них в своем выходе, не стесняйтесь использовать это...
#include <string>
#include <sstream>
std::string escapeJsonString(const std::string& input) {
std::ostringstream ss;
for (auto iter = input.cbegin(); iter != input.cend(); iter++) {
//C++98/03:
//for (std::string::const_iterator iter = input.begin(); iter != input.end(); iter++) {
switch (*iter) {
case '\\': ss << "\\\\"; break;
case '"': ss << "\\\""; break;
case '/': ss << "\\/"; break;
case '\b': ss << "\\b"; break;
case '\f': ss << "\\f"; break;
case '\n': ss << "\\n"; break;
case '\r': ss << "\\r"; break;
case '\t': ss << "\\t"; break;
default: ss << *iter; break;
}
}
return ss.str();
}
Ответ 2
Caveat
Независимо от того, какое решение вы принимаете, имейте в виду, что стандарт JSON требует, чтобы вы удалили все управляющие символы. Это кажется распространенным заблуждением. Многие разработчики ошибаются.
Все управляющие символы означают все: от '\x00'
до '\x1f'
, а не только те с коротким представлением, как '\x0a'
(также известный как '\n'
). Например, вы должны избегать символа '\x02'
как \u0002
.
См. также: ECMA-404 Формат обмена данными JSON, Страница 10
Простое решение
Если вы точно знаете, что ваша строка ввода закодирована в кодировке UTF-8, вы можете держать вещи простыми.
Поскольку JSON позволяет вам избежать всего через \uXXXX
, даже "
и \
, простое решение:
#include <sstream>
#include <iomanip>
std::string escape_json(const std::string &s) {
std::ostringstream o;
for (auto c = s.cbegin(); c != s.cend(); c++) {
if (*c == '"' || *c == '\\' || ('\x00' <= *c && *c <= '\x1f')) {
o << "\\u"
<< std::hex << std::setw(4) << std::setfill('0') << (int)*c;
} else {
o << *c;
}
}
return o.str();
}
Кратчайшее представление
Для кратчайшего представления вы можете использовать ярлыки JSON, такие как \"
вместо \u0022
. Следующая функция создает кратчайшее JSON-представление кодированной строки UTF-8 s
:
#include <sstream>
#include <iomanip>
std::string escape_json(const std::string &s) {
std::ostringstream o;
for (auto c = s.cbegin(); c != s.cend(); c++) {
switch (*c) {
case '"': o << "\\\""; break;
case '\\': o << "\\\\"; break;
case '\b': o << "\\b"; break;
case '\f': o << "\\f"; break;
case '\n': o << "\\n"; break;
case '\r': o << "\\r"; break;
case '\t': o << "\\t"; break;
default:
if ('\x00' <= *c && *c <= '\x1f') {
o << "\\u"
<< std::hex << std::setw(4) << std::setfill('0') << (int)*c;
} else {
o << *c;
}
}
}
return o.str();
}
Чистая инструкция switch
Можно также согласиться с чистым оператором switch, то есть без if
и <iomanip>
. Хотя это довольно громоздко, это может быть предпочтительнее с точки зрения "безопасности по простоте и чистоте":
#include <sstream>
std::string escape_json(const std::string &s) {
std::ostringstream o;
for (auto c = s.cbegin(); c != s.cend(); c++) {
switch (*c) {
case '\x00': o << "\\u0000"; break;
case '\x01': o << "\\u0001"; break;
...
case '\x0a': o << "\\n"; break;
...
case '\x1f': o << "\\u001f"; break;
case '\x22': o << "\\\""; break;
case '\x5c': o << "\\\\"; break;
default: o << *c;
}
}
return o.str();
}
Использование библиотеки
Возможно, вам стоит взглянуть на https://github.com/nlohmann/json, который является эффективной только для С++ библиотеки (MIT License), которая кажется быть очень хорошо протестированным.
Вы можете либо вызвать их метод escape_string()
напрямую, либо выполнить свою реализацию escape_string()
в качестве отправной точки для своей собственной реализации:
https://github.com/nlohmann/json/blob/ec7a1d834773f9fee90d8ae908a0c9933c5646fc/src/json.hpp#L4604-L4697
Ответ 3
Я написал простую функцию JSON escape и unescaped.
Код доступен в GitHub.
Для всех, кто интересуется здесь, это код:
enum State {ESCAPED, UNESCAPED};
std::string escapeJSON(const std::string& input)
{
std::string output;
output.reserve(input.length());
for (std::string::size_type i = 0; i < input.length(); ++i)
{
switch (input[i]) {
case '"':
output += "\\\"";
break;
case '/':
output += "\\/";
break;
case '\b':
output += "\\b";
break;
case '\f':
output += "\\f";
break;
case '\n':
output += "\\n";
break;
case '\r':
output += "\\r";
break;
case '\t':
output += "\\t";
break;
case '\\':
output += "\\\\";
break;
default:
output += input[i];
break;
}
}
return output;
}
std::string unescapeJSON(const std::string& input)
{
State s = UNESCAPED;
std::string output;
output.reserve(input.length());
for (std::string::size_type i = 0; i < input.length(); ++i)
{
switch(s)
{
case ESCAPED:
{
switch(input[i])
{
case '"':
output += '\"';
break;
case '/':
output += '/';
break;
case 'b':
output += '\b';
break;
case 'f':
output += '\f';
break;
case 'n':
output += '\n';
break;
case 'r':
output += '\r';
break;
case 't':
output += '\t';
break;
case '\\':
output += '\\';
break;
default:
output += input[i];
break;
}
s = UNESCAPED;
break;
}
case UNESCAPED:
{
switch(input[i])
{
case '\\':
s = ESCAPED;
break;
default:
output += input[i];
break;
}
}
}
}
return output;
}
Ответ 4
Изначально вы не сказали точно, откуда берутся эти нити, которые вы собираете вместе, так что это может быть бесполезно. Но если все они живут в коде, как @isnullxbh упомянул в этом комментарии для ответа на другой вопрос, другой вариант - использовать прекрасную функцию C++ 11: необработанные строковые литералы.
Я не буду цитировать длинное, основанное на стандартах объяснение cppreference, вы можете прочитать его там. Однако в основном R-строки приводят к C++ тому же виду литералов, разделенных программистами, без каких-либо ограничений по содержанию, которые языки, такие как Perl, используют так эффективно. (Они могут быть одним из величайших изобретений Perl :)
my qstring = q{Quoted 'string'!};
my qqstring = qq{Double "quoted" 'string'!};
my replacedstring = q{Regexps that /totally/! get eaten by your parser.};
replacedstring =~ s{/totally/!}{(won't!)};
# Heh. I see the syntax highlighter isn't quite up to the challege, though.
В C++ 11 или более поздних версиях необработанного строкового литерала ставится префикс заглавной буквы R перед двойными кавычками, а внутри строки вводится разделитель свободной формы (один или несколько символов), за которым следует открывающая часть.
С этого момента вы можете писать буквально все, что угодно, кроме закрывающей пары, за которой следует выбранный вами разделитель. Эта последовательность (за которой следует закрывающая двойная кавычка) завершает необработанный литерал, и тогда у вас есть std::string
которому вы можете с уверенностью доверять, и при любом разборе или обработке строки он останется беспрепятственным.
"Сырой" -ness также не теряется при последующих манипуляциях. Итак, заимствуя из списка глав для Crockford Как работает JavaScript, это полностью верно:
std::string ch0_to_4 = R"json(
[
{"number": 0, "chapter": "Read Me First!"},
{"number": 1, "chapter": "How Names Work"},
{"number": 2, "chapter": "How Numbers Work"},
{"number": 3, "chapter": "How Big Integers Work"},
{"number": 4, "chapter": "How Big Floating Point Works"},)json";
std::string ch5_and_6 = R"json(
{"number": 5, "chapter": "How Big Rationals Work"},
{"number": 6, "chapter": "How Booleans Work"})json";
std::string chapters = ch0_to_4 + ch5_and_6 + "\n]";
std::cout << chapters;
Строка 'chapters' появится из std::cout
полностью без изменений:
[
{"number": 0, "chapter": "Read Me First!"},
{"number": 1, "chapter": "How Names Work"},
{"number": 2, "chapter": "How Numbers Work"},
{"number": 3, "chapter": "How Big Integers Work"},
{"number": 4, "chapter": "How Big Floating Point Works"},
{"number": 5, "chapter": "How Big Rationals Work"},
{"number": 6, "chapter": "How Booleans Work"}
]