¿Escape de cadena JSON simple para C ++?

Tengo un progtwig muy simple que genera una cadena JSON simple que concatené de forma manual y salida a través de la secuencia std :: cout (la salida es realmente simple) pero tengo cadenas que pueden contener comillas dobles, llaves y Otros caracteres que podrían romper la cadena JSON. Así que necesito una biblioteca (o una función más precisa) para escapar de las cadenas de acuerdo con el estándar JSON, lo más ligero posible, nada más, nada menos.

Encontré algunas bibliotecas que se utilizan para codificar objetos enteros en JSON, pero teniendo en cuenta que mi progtwig es un archivo cpp de 900 líneas, prefiero no confiar en una biblioteca que es varias veces más grande que mi progtwig solo para lograr algo tan simple como esta.

Actualización : ¡No uses esto! vog ofrece una solución mucho más completa (e igualmente compacta) más abajo: https://stackoverflow.com/a/33799784

Este es un comienzo muy simple, aunque no maneja caracteres unicode no válidos. Si no espera ninguno de ellos en su salida, siéntase libre de usar esto …

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

Advertencia

Sea cual sea la solución que tome, tenga en cuenta que el estándar JSON requiere que escape todos los caracteres de control . Esto parece ser un error común. Muchos desarrolladores se equivocan.

Todos los caracteres de control significan todo desde '\x00' hasta '\x1f' , no solo aquellos con una representación corta como '\x0a' (también conocida como '\n' ). Por ejemplo, debe escapar el carácter '\x02' como \u0002 .

Vea también: ECMA-404 El formato de intercambio de datos JSON , página 10

Solución simple

Si está seguro de que su cadena de entrada está codificada en UTF-8, puede mantener las cosas simples.

Como JSON le permite escapar de todo a través de \uXXXX , incluso " y \ , una solución simple es:

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

Representación más corta

Para la representación más corta, puede usar accesos directos JSON, como \" lugar de \u0022 . La siguiente función produce la representación JSON más corta de una cadena codificada en UTF-8:

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

Declaración de cambio puro

También es posible llevarse bien con una instrucción de cambio puro, es decir, sin if y . Si bien esto es bastante engorroso, puede ser preferible desde el punto de vista de "seguridad por simplicidad y pureza":

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

Usando una biblioteca

Es posible que desee echar un vistazo a https://github.com/nlohmann/json , que es una biblioteca eficiente de C ++ solo para encabezado (licencia MIT) que parece estar muy bien probada.

Puede llamar directamente a su método escape_string() , o puede tomar su implementación de escape_string() como punto de partida para su propia implementación:

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

He escrito un simple escape JSON y funciones no escapadas. El código es público disponible en GitHub . Para cualquier persona interesada aquí está el código:

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