Question Échappement de chaîne JSON simple pour C ++?


Je vais avoir un programme très simple qui génère simple chaîne JSON que je concaténer manuellement ensemble et sortie à travers le flux std :: Cout (la sortie est vraiment simple) mais j'ai des chaînes qui pourraient contenir des guillemets doubles, des accolades et d'autres caractères susceptibles de casser la chaîne JSON. J'ai donc besoin d'une bibliothèque (ou d'une fonction plus précise) pour échapper aux chaînes conformément à la norme JSON, aussi légère que possible, rien de plus, rien de moins.

J'ai trouvé quelques bibliothèques qui sont utilisés pour coder des objets entiers dans JSON, mais ayant à l'esprit mon programme est cpp 900 ligne, je veux plutôt de ne pas compter sur une bibliothèque qui est plusieurs fois plus grand que mon programme juste pour obtenir quelque chose d'aussi simple que ce.


29
2017-10-11 10:16


origine


Réponses:


Mettre à jour: Ne pas utiliser ça! vog propose une solution beaucoup plus complète (et tout aussi compacte): https://stackoverflow.com/a/33799784

Ici, le démarrage est très simple, mais il ne gère pas les caractères Unicode non valides. Si vous n'en attendez aucun dans votre sortie, n'hésitez pas à l'utiliser ...

#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();
}

31
2017-10-11 11:26



Caveat

Quelle que soit la solution que vous prenez, gardez à l’esprit que la norme JSON exige que vous vous échappiez tous les caractères de contrôle. Cela semble être une idée fausse commune. Beaucoup de développeurs se trompent.

Tous les caractères de contrôle signifie tout de '\x00' à '\x1f', pas seulement ceux avec une courte représentation telle que '\x0a' (aussi connu sous le nom '\n'). Par exemple, vous doit s'échapper la '\x02' caractère comme \u0002.

Voir également: ECMA-404 Le format d'échange de données JSON, Page 10

Solution simple

Si vous savez avec certitude que votre chaîne d'entrée est encodée en UTF-8, vous pouvez garder les choses simples.

Puisque JSON vous permet d’échapper à tout via \uXXXXmême " et \, une solution simple est:

#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();
}

Représentation la plus courte

Pour la représentation la plus courte, vous pouvez utiliser les raccourcis JSON, tels que \" au lieu de \u0022. La fonction suivante produit la représentation JSON la plus courte d'une chaîne codée 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();
}

Déclaration pure switch

Il est également possible de s’entendre avec un énoncé de commutateur pur, c’est-à-dire sans if et <iomanip>. Bien que cela soit assez lourd, il peut être préférable, du point de vue de la "sécurité par la simplicité et de la pureté":

#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();
}

Utiliser une bibliothèque

Vous voudrez peut-être regarder https://github.com/nlohmann/json, qui est une librairie C ++ (Licence MIT) efficace, qui semble être très bien testée.

Vous pouvez soit appeler leur escape_string() méthode directement, ou vous pouvez prendre leur implémentation de escape_string() comme point de départ pour votre propre implémentation:

https://github.com/nlohmann/json/blob/ec7a1d834773f9fee90d8ae908a0c9933c5646fc/src/json.hpp#L4604-L4697


31
2017-11-19 09:25



J'ai écrit une simple évasion JSON et des fonctions non échappées. Le code est public disponible dans GitHub. Pour toute personne intéressée, voici le code:

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;
}

7
2017-10-28 13:39