Come devo evitare le stringhe in JSON?


154

Quando si creano manualmente i dati JSON, come devo evitare i campi stringa? Dovrei usare qualcosa di simile di Apache Commons Lang StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmlo dovrei usare java.net.URLEncoder?

Il problema è che quando uso SEU.escapeHtml, non sfugge alle virgolette e quando avvolgo l'intera stringa in una coppia di 's, verrà generato un JSON non valido.


20
Se stai avvolgendo l'intera stringa in una coppia di ', sei condannato dall'inizio: le stringhe JSON possono essere solo circondate ". Vedi ietf.org/rfc/rfc4627.txt .
Thanatos,

2
+1 per il StringEscapeUtilitiescontorno. È abbastanza utile.
Muhammad Gelbana,

Risposte:


157

Idealmente, trova una libreria JSON nella tua lingua in cui puoi alimentare una struttura dati appropriata e lascia che si preoccupi di come sfuggire alle cose . Ti manterrà molto più sano. Se per qualsiasi motivo non hai una libreria nella tua lingua, non vuoi usarne una (non la suggerirei¹), o stai scrivendo una libreria JSON, continua a leggere.

Fuggire secondo la RFC. JSON è abbastanza liberale: Gli unici caratteri si deve fuggire sono \, "e codici di controllo (niente di meno che U + 0020).

Questa struttura di escape è specifica di JSON. Avrai bisogno di una funzione specifica JSON. Tutte le escape possono essere scritte come \uXXXXdove si XXXXtrova l'unità di codice UTF-16¹ per quel personaggio. Ci sono alcune scorciatoie, come \\, che funzionano anche. (E si traducono in un output più piccolo e più chiaro.)

Per i dettagli completi, consultare RFC .

¹ L'escaping di JSON è basato su JS, quindi utilizza \uXXXX, dove si XXXXtrova un'unità di codice UTF-16. Per i punti di codice al di fuori del BMP, ciò significa codificare coppie surrogate, che possono diventare un po 'pelose. (Oppure, puoi semplicemente generare direttamente il carattere, poiché JSON è codificato per essere testo Unicode e consente questi caratteri particolari.)


È valido in JSON, come in JavaScript, racchiudere le stringhe tra virgolette doppie o virgolette singole? O è valido racchiuderli tra virgolette doppie?
Behrang Saeedzadeh,

14
Solo virgolette doppie ( ").
Thanatos,

3
@Sergei: i personaggi {[]}:?non devono essere salvati con una singola barra rovesciata. ( \:, ad esempio, non è valido in una stringa JSON.) Tutti questi possono facoltativamente essere salvati utilizzando la \uXXXXsintassi, a scapito di diversi byte. Vedi §2.5 della RFC.
Thanatos,

2
Non sono sicuro di quanto ampiamente sia supportato, ma nella mia esperienza una chiamata a JSON.stringify()fare il lavoro.
LS

2
@BitTickler un carattere unicode non è affatto vago - significa solo che ha un punto (o punti) di codice nelle specifiche unicode. Quando usi std :: string, è un gruppo di caratteri unicode. Quando è necessario serializzarlo, diciamo a un file o attraverso la rete, è qui che entra in gioco "quale codifica". Secondo Thanatos sembra che vogliano usare un UTF, ma tecnicamente qualsiasi codifica può essere usata fintanto che può essere ricostituito in caratteri unicode.
Gerard ONeill,

54

Estratto da Jettison :

 public static String quote(String string) {
         if (string == null || string.length() == 0) {
             return "\"\"";
         }

         char         c = 0;
         int          i;
         int          len = string.length();
         StringBuilder sb = new StringBuilder(len + 4);
         String       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();
     }

10
Bene, questo era il tag OP
MonoThreaded

Non capire solo quando c <'', cambia in \ u. Nel mio caso, c'è il carattere \ uD38D, che è 55357 e oltre '', quindi non cambia in \ u ...
Stony

1
@Stony Sembra una nuova domanda
MonoThreaded

@MonoThreaded Grazie per la risposta, non so ancora perché. ma alla fine ho modificato il metodo per risolverlo come di seguito, if (c <'' || c> 0x7f) {t = "000" + Integer.toHexString (c) .toUpperCase (); sb.append ("\\ u" + t.substring (t.length () - 4)); } else {sb.append (c); }}
Stony

1
@Stony, tutti i caratteri diversi da ", \ e i caratteri di controllo (quelli prima di "") sono validi all'interno delle stringhe JSON purché la codifica di output corrisponda. In altre parole, non è necessario codificare “펍” \uD38Dfinché la codifica UTF è conservata.
Meustrus,

37

Prova questo org.codehaus.jettison.json.JSONObject.quote("your string").

Scaricalo qui: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison


Sicuramente la soluzione migliore! Thx
Lastnico,

ma questo non cita parentesi graffe come [{
Sergei

1
@Sergei Non è necessario sfuggire alle parentesi graffe all'interno di una stringa JSON.
Yobert,

Potrebbe essere utile mostrare ciò che effettivamente ritorna.
Trevor,

2
org.json.JSONObject.quote ("your json string") funziona anche bene
webjockey

23

org.json.simple.JSONObject.escape () sfugge alle virgolette, \, /, \ r, \ n, \ b, \ f, \ te altri caratteri di controllo. Può essere usato per sfuggire ai codici JavaScript.

import org.json.simple.JSONObject;
String test =  JSONObject.escape("your string");

3
Dipende dalla libreria json che stai utilizzando (JSONObject.escape, JSONObject.quote, ..) ma è sempre un metodo statico che fa il lavoro di quotazione e semplicemente dovrebbe essere riutilizzato
ammina

Di quale biblioteca fa parte org.json? Non ce l'ho sul mio percorso di classe.
Alex Spurling,


22

Apache Commons Lang ora supporta questo. Assicurati solo di avere una versione abbastanza recente delle lingue comuni di Apache sul tuo percorso di classe. Avrai bisogno della versione 3.2+

Note di rilascio per la versione 3.2

LANG-797: Aggiunto escape / unescapeJson a StringEscapeUtils.


Questa è la risposta più pratica per me. La maggior parte dei progetti usa già apache commons lang, quindi non è necessario aggiungere una dipendenza per una funzione. Un costruttore JSON sarebbe probabilmente la risposta migliore.
fabbri il

Come follow-up, e poiché non riesco a capire come modificare un commento ne ho aggiunto uno nuovo, ho trovato javax.json.JsonObjectBuilder e javax.json.JsonWriter. Molto bella combinazione costruttore / scrittore.
Absmiths,

1
Questo è deprecato in apache commons lang, devi usare il testo di apache commons . Purtroppo, questa libreria segue le specifiche opzionali / obsolete sfuggendo ai /caratteri. Questo rompe molte cose tra cui JSON con URL all'interno. La proposta originale aveva /come carattere speciale la fuga, ma non è più così, come possiamo vedere nelle ultime specifiche al momento della stesura
adamnfish

10

org.json.JSONObject quote(String data) il metodo fa il lavoro

import org.json.JSONObject;
String jsonEncodedString = JSONObject.quote(data);

Estratto dalla documentazione:

Codifica i dati come una stringa JSON. Questo applica le virgolette e l'eventuale fuga di caratteri necessaria . [...] Null verrà interpretato come una stringa vuota


1
org.apache.sling.commons.json.JSONObjectha anche la stessa cosa
Jordan Shurmer,

5

StringEscapeUtils.escapeJavaScript/ StringEscapeUtils.escapeEcmaScriptdovrei fare anche il trucco.


10
escapeJavaScriptsfugge alle virgolette singole come \', che non è corretto.
laurt,

4

Se si utilizza fastexml jackson, è possibile utilizzare quanto segue: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

Se si utilizza codehaus jackson, è possibile utilizzare quanto segue: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


3

Non sei sicuro di cosa intendi per "creazione manuale di json", ma puoi usare qualcosa come gson ( http://code.google.com/p/google-gson/ ) e che trasformerebbe il tuo HashMap, Array, String, ecc. , a un valore JSON. Consiglio di andare con un framework per questo.


2
Con manuale intendevo non usare una libreria JSON come Simple JSON, Gson o XStream.
Behrang Saeedzadeh,

Solo una questione di curiosità: perché non dovresti usare una di queste API? È come cercare di sfuggire agli URL manualmente, invece di utilizzare URLEncode / Decode ...
Vladimir

1
Non proprio lo stesso, quelle librerie hanno molto di più dell'equivalente di URLEncode / Decode, includono un intero pacchetto di serializzazione per consentire la persistenza dell'oggetto java in forma json, e a volte basta davvero codificare un breve gruppo di testo
jmd

2
fare una creazione manuale di JSON ha senso, se non si desidera includere una libreria solo per serializzare piccoli frammenti di dati
Aditya Kumar Pandey

2
Vorrei chiedere la rimozione di un membro del team da qualsiasi progetto in cui mi trovavo se avessero osato creare manualmente JSON dove esisteva una libreria di alta qualità per farlo.
Michael Joyce,

2

Non ho speso il tempo per accertarmi al 100%, ma ha funzionato abbastanza per i miei input da essere accettato dai validatori JSON online:

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

anche se non sembra migliore di org.codehaus.jettison.json.JSONObject.quote("your string")

Uso semplicemente gli strumenti di velocità nel mio progetto: il mio edificio "manuale JSON" era all'interno di un modello di velocità


2

Per coloro che sono venuti qui alla ricerca di una soluzione da riga di comando, come me, il codice -data-urlencode di cURL funziona bene:

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

invia

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

, per esempio. I dati JSON più grandi possono essere inseriti in un file e useresti la sintassi @ per specificare un file da cui assimilare i dati da cui fuggire. Ad esempio, se

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

tu useresti

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

E ora, questo è anche un tutorial su come interrogare Freebase dalla riga di comando :-)


2

Utilizzare la classe EscapeUtils nell'API lang di commons.

EscapeUtils.escapeJavaScript("Your JSON string");

1
Si noti che le virgolette singole, ad esempio, vengono gestite in modo diverso quando si passa a javascript o json. In commons.lang 3.4 StringEscapeUtils ( commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/… ) ha un metodo escapeJSON che è diverso dal metodo escapeJavaScript in commons.lang 2: commons.apache. org / proper / commons-lang / javadocs / api-2.6 / org /…
GlennV

1

Considera la classe JsonWriter di Moshi . Ha un'API meravigliosa e riduce al minimo la copia, tutto può essere trasmesso in streaming su un archiviato, OutputStream, ecc.

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

Se vuoi la stringa in mano:

Buffer b = new Buffer(); // okio.Buffer
JsonWriter writer = new JsonWriter(b);
//...
String jsonString = b.readUtf8();


0

Se devi uscire da JSON all'interno della stringa JSON, usa org.json.JSONObject.quote ("la tua stringa json che deve essere salvata") sembra funzionare bene


0

l'utilizzo della sintassi \ uXXXX può risolvere questo problema, google UTF-16 con il nome del segno, puoi scoprire XXXX, ad esempio: utf-16 double quote


0

I metodi qui che mostrano l'implementazione effettiva sono tutti difettosi.
Non ho un codice Java, ma solo per la cronaca, potresti facilmente convertire questo codice C #:

Per gentile concessione del mono-project @ https://github.com/mono/mono/blob/master/mcs/class/System.Web/System.Web/HttpUtility.cs

public static string JavaScriptStringEncode(string value, bool addDoubleQuotes)
{
    if (string.IsNullOrEmpty(value))
        return addDoubleQuotes ? "\"\"" : string.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();
}

Questo può essere compattato

    // https://github.com/mono/mono/blob/master/mcs/class/System.Json/System.Json/JsonValue.cs
public class SimpleJSON
{

    private static  bool NeedEscape(string 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 "</" for <script> 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();
    }
}

In che modo il quote()metodo descritto in altre risposte è difettoso?
Sandy,

0

Penso che la migliore risposta nel 2017 sia utilizzare le API javax.json. Usa javax.json.JsonBuilderFactory per creare i tuoi oggetti json, quindi scrivi gli oggetti usando javax.json.JsonWriterFactory. Molto bella combinazione costruttore / scrittore.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.