Comment devrais-je échapper des chaînes en JSON?

Lors de la création manuelle de données JSON, comment dois-je échapper des champs de chaîne? Dois-je utiliser quelque chose comme SsortingngEscapeUtilities.escapeHtml , SsortingngEscapeUtilities.escapeXml Apache Commons Lang ou devrais-je utiliser java.net.URLEncoder ?

Le problème est que quand j’utilise SEU.escapeHtml , cela n’échappe pas aux guillemets et quand j’emballe la chaîne entière dans une paire de s, un JSON mal formé sera généré.

Idéalement, trouvez une bibliothèque JSON dans votre langue que vous pouvez utiliser pour alimenter une structure de données appropriée, et laissez-la vous soucier de la manière d’échapper aux choses . Ça va te garder plus sain. Si pour une raison quelconque vous n’avez pas de bibliothèque dans votre langue, vous ne voulez pas en utiliser une (je ne le suggérerais pas¹), ou vous écrivez une bibliothèque JSON, lisez la suite.

Échappez-vous selon la RFC. JSON est plutôt libéral: Les seuls caractères que vous devez échapper sont \ , " , et les codes de contrôle (rien de moins que U + 0020).

Cette structure d’échappement est spécifique à JSON. Vous aurez besoin d’une fonction spécifique JSON. Toutes les échappements peuvent être écrits sous la forme \uXXXXXXXX est l’unité de code UTF-16¹ pour ce caractère. Il existe quelques raccourcis, tels que \\ , qui fonctionnent également. (Et ils se traduisent par une sortie plus petite et plus claire.)

Pour plus de détails, voir la RFC .

L’échappement de JSON est construit sur JS, il utilise donc \uXXXX , où XXXX est une unité de code UTF-16. Pour les points de code en dehors du BMP, cela signifie encoder des paires de substitution, qui peuvent devenir un peu poilues. (Ou bien, vous pouvez simplement sortir le caractère directement, car les codages JSON sont du texte Unicode et autorisent ces caractères particuliers.)

Extrait de Jettison :

  public static Ssortingng quote(Ssortingng ssortingng) { if (ssortingng == null || ssortingng.length() == 0) { return "\"\""; } char c = 0; int i; int len = ssortingng.length(); SsortingngBuilder sb = new SsortingngBuilder(len + 4); Ssortingng t; sb.append('"'); for (i = 0; i < len; i += 1) { c = string.charAt(i); switch (c) { case '\\': case '"': sb.append('\\'); sb.append(c); break; case '/': // if (b == '<') { sb.append('\\'); // } sb.append(c); break; case '\b': sb.append("\\b"); break; case '\t': sb.append("\\t"); break; case '\n': sb.append("\\n"); break; case '\f': sb.append("\\f"); break; case '\r': sb.append("\\r"); break; default: if (c < ' ') { t = "000" + Integer.toHexString(c); sb.append("\\u" + t.substring(t.length() - 4)); } else { sb.append(c); } } } sb.append('"'); return sb.toString(); } 

Essayez cette org.codehaus.jettison.json.JSONObject.quote("your ssortingng") .

Téléchargez-le ici: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison

org.json.simple.JSONObject.escape () échappe aux guillemets, \, /, \ r, \ n, \ b, \ f, \ t et aux autres caractères de contrôle. Il peut être utilisé pour échapper à des codes JavaScript.

 import org.json.simple.JSONObject; Ssortingng test = JSONObject.escape("your ssortingng"); 

Apache commons lang supporte maintenant ceci. Assurez-vous simplement d’avoir une version assez récente d’Apache commons lang sur votre classpath. Vous aurez besoin de la version 3.2+

Notes de publication pour la version 3.2

LANG-797: Ajout de escape / unescapeJson à SsortingngEscapeUtils.

org.json.JSONObject quote(Ssortingng data) fait le travail

 import org.json.JSONObject; Ssortingng jsonEncodedSsortingng = JSONObject.quote(data); 

Extrait de la documentation:

Encode les données en tant que chaîne JSON. Ceci applique des citations et tout caractère nécessaire pour s’échapper . […] Null sera interprété comme une chaîne vide

SsortingngEscapeUtils.escapeJavaScript / SsortingngEscapeUtils.escapeEcmaScript devrait également faire l’affaire.

Vous ne savez pas ce que vous entendez par “créer json manuellement”, mais vous pouvez utiliser quelque chose comme gson ( http://code.google.com/p/google-gson/ ), qui transformera votre HashMap, Array, Ssortingng, etc. , à une valeur JSON. Je recommande d’aller avec un cadre pour cela.

Je n’ai pas passé le temps à être sûr à 100%, mais cela a fonctionné pour mes entrées pour être accepté par les validateurs JSON en ligne:

 org.apache.velocity.tools.generic.EscapeTool.EscapeTool().java("input") 

bien qu’il ne soit pas meilleur que org.codehaus.jettison.json.JSONObject.quote("your ssortingng")

J’utilise déjà des outils de vélocité dans mon projet – mon bâtiment “JSON manuel” était dans un modèle de vélocité

Pour ceux qui sont venus ici à la recherche d’une solution en ligne de commande, comme moi, cdLL -data-urlencode fonctionne bien:

 curl -G -v -s --data-urlencode 'query={"type" : "/music/artist"}' 'https://www.googleapis.com/freebase/v1/mqlread' 

envoie

 GET /freebase/v1/mqlread?query=%7B%22type%22%20%3A%20%22%2Fmusic%2Fartist%22%7D HTTP/1.1 

, par exemple. Des données JSON plus volumineuses peuvent être placées dans un fichier et vous devez utiliser la syntaxe @ pour spécifier un fichier à extraire des données à échapper. Par exemple, si

 $ cat 1.json { "type": "/music/artist", "name": "The Police", "album": [] } 

vous utiliseriez

 curl -G -v -s --data-urlencode query@1.json 'https://www.googleapis.com/freebase/v1/mqlread' 

Et maintenant, c’est aussi un tutoriel sur la façon d’interroger Freebase depuis la ligne de commande 🙂

Utilisez la classe EscapeUtils dans l’API commons lang.

 EscapeUtils.escapeJavaScript("Your JSON ssortingng"); 

Si vous utilisez fastexml jackson, vous pouvez utiliser les éléments suivants: com.fasterxml.jackson.core.io.JsonSsortingngEncoder.getInstance().quoteAsSsortingng(input)

Si vous utilisez codehaus jackson, vous pouvez utiliser les éléments suivants: org.codehaus.jackson.io.JsonSsortingngEncoder.getInstance().quoteAsSsortingng(input)

Considérons la classe JsonWriter de Moshi . Il a une API merveilleuse et réduit au minimum la copie, tout peut être bien diffusé vers un fichier, OutputStream, etc.

 OutputStream os = ...; JsonWriter json = new JsonWriter(Okio.buffer(Okio.sink(os))); json.beginObject(); json.name("id").value(getId()); json.name("scores"); json.beginArray(); for (Double score : getScores()) { json.value(score); } json.endArray(); json.endObject(); 

Si vous voulez la chaîne en main:

 Buffer b = new Buffer(); // okio.Buffer JsonWriter writer = new JsonWriter(b); //... Ssortingng jsonSsortingng = b.readUtf8(); 

Si vous avez besoin d’échapper à JSON dans la chaîne JSON, utilisez org.json.JSONObject.quote (“votre chaîne json devant être échappée”) semble bien fonctionner

en utilisant la syntaxe \ uXXXX peut résoudre ce problème, google UTF-16 avec le nom du signe, vous pouvez trouver XXXX, par exemple: guillemet double utf-16

Les méthodes ici qui montrent la mise en œuvre réelle sont toutes défectueuses.
Je n’ai pas de code Java, mais pour l’enregistrement, vous pouvez facilement convertir ce code C #:

Gracieuseté du projet mono @ https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs

 public static ssortingng JavaScriptSsortingngEncode(ssortingng value, bool addDoubleQuotes) { if (ssortingng.IsNullOrEmpty(value)) return addDoubleQuotes ? "\"\"" : ssortingng.Empty; int len = value.Length; bool needEncode = false; char c; for (int i = 0; i < len; i++) { c = value[i]; if (c >= 0 && c < = 31 || c == 34 || c == 39 || c == 60 || c == 62 || c == 92) { needEncode = true; break; } } if (!needEncode) return addDoubleQuotes ? "\"" + value + "\"" : value; var sb = new System.Text.StringBuilder(); if (addDoubleQuotes) sb.Append('"'); for (int i = 0; i < len; i++) { c = value[i]; if (c >= 0 && c < = 7 || c == 11 || c >= 14 && c < = 31 || c == 39 || c == 60 || c == 62) sb.AppendFormat("\\u{0:x4}", (int)c); else switch ((int)c) { case 8: sb.Append("\\b"); break; case 9: sb.Append("\\t"); break; case 10: sb.Append("\\n"); break; case 12: sb.Append("\\f"); break; case 13: sb.Append("\\r"); break; case 34: sb.Append("\\\""); break; case 92: sb.Append("\\\\"); break; default: sb.Append(c); break; } } if (addDoubleQuotes) sb.Append('"'); return sb.ToString(); } 

Cela peut être compacté dans

  // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs public class SimpleJSON { private static bool NeedEscape(ssortingng src, int i) { char c = src[i]; return c < 32 || c == '"' || c == '\\' // Broken lead surrogate || (c >= '\uD800' && c < = '\uDBFF' && (i == src.Length - 1 || src[i + 1] < '\uDC00' || src[i + 1] > '\uDFFF')) // Broken tail surrogate || (c >= '\uDC00' && c < = '\uDFFF' && (i == 0 || src[i - 1] < '\uD800' || src[i - 1] > '\uDBFF')) // To produce valid JavaScript || c == '\u2028' || c == '\u2029' // Escape " tags || (c == '/' && i > 0 && src[i - 1] == '< '); } public static string EscapeString(string src) { System.Text.StringBuilder sb = new System.Text.StringBuilder(); int start = 0; for (int i = 0; i < src.Length; i++) if (NeedEscape(src, i)) { sb.Append(src, start, i - start); switch (src[i]) { case '\b': sb.Append("\\b"); break; case '\f': sb.Append("\\f"); break; case '\n': sb.Append("\\n"); break; case '\r': sb.Append("\\r"); break; case '\t': sb.Append("\\t"); break; case '\"': sb.Append("\\\""); break; case '\\': sb.Append("\\\\"); break; case '/': sb.Append("\\/"); break; default: sb.Append("\\u"); sb.Append(((int)src[i]).ToString("x04")); break; } start = i + 1; } sb.Append(src, start, src.Length - start); return sb.ToString(); } } 

Je pense que la meilleure réponse en 2017 est d’utiliser les API javax.json. Utilisez javax.json.JsonBuilderFactory pour créer vos objects json, puis écrivez les objects à l’aide de javax.json.JsonWriterFactory. Très belle combinaison constructeur / écrivain.

Apache commons-text a maintenant un SsortingngEscapeUtils.escapeJson (Ssortingng) .