Подтвердить что ты не робот

Простой вывод строки JSON для С++?

У меня есть очень простая программа, которая выводит простую строку JSON, которую я вручную объединяю и выводю через поток std:: cout (выход действительно такой простой), но у меня есть строки, которые могут содержать двойные кавычки, фигурные -браски и другие символы, которые могут сломать строку JSON. Поэтому мне нужна библиотека (или функция более точно), чтобы избежать строк в соответствии со стандартом JSON, насколько это возможно, не более, не что иное.

Я нашел несколько библиотек, которые используются для кодирования целых объектов в JSON, но имея в виду, что моя программа - 900 строк cpp файла, я скорее не хочу полагаться на библиотеку, которая в несколько раз больше, чем моя программа, просто для достижения чего-то так же просто, как это.

4b9b3361

Ответ 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"}
]