Wie soll ich Strings in JSON entkommen?


154

Wie soll ich beim manuellen Erstellen von JSON-Daten Zeichenfolgenfelder umgehen? Sollte ich so etwas wie Apache Commons Lang StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmloder soll ich verwenden java.net.URLEncoder?

Das Problem ist, dass bei der Verwendung SEU.escapeHtmlkeine Anführungszeichen entstehen und wenn ich die gesamte Zeichenfolge in ein Paar von 's einbinde, wird ein fehlerhafter JSON generiert.


20
Wenn Sie die gesamte Zeichenfolge in ein Paar 'einschließen, sind Sie von Anfang an zum Scheitern verurteilt: JSON-Zeichenfolgen können nur mit umgeben werden ". Siehe ietf.org/rfc/rfc4627.txt .
Thanatos

2
+1 für die StringEscapeUtilitiesGliederung. Es ist ziemlich nützlich.
Muhammad Gelbana

Antworten:


157

Suchen Sie im Idealfall eine JSON-Bibliothek in Ihrer Sprache , der Sie eine geeignete Datenstruktur zuführen können, und lassen Sie sie sich Gedanken darüber machen, wie Sie den Dingen entkommen können . Es wird dich viel vernünftiger halten. Wenn Sie aus irgendeinem Grund keine Bibliothek in Ihrer Sprache haben, keine verwenden möchten (ich würde dies nicht vorschlagen¹) oder eine JSON-Bibliothek schreiben, lesen Sie weiter.

Entkomme es gemäß dem RFC. JSON ist ziemlich liberal: Die einzigen Zeichen , die Sie müssen sind entkommen \, "und Tonfolgen (etwas weniger als U + 0020).

Diese Escape-Struktur ist spezifisch für JSON. Sie benötigen eine JSON-spezifische Funktion. Alle Escapezeichen können so geschrieben werden, \uXXXXdass XXXXsich die UTF-16-Codeeinheit¹ für dieses Zeichen befindet. Es gibt einige Verknüpfungen, z. B. \\, die ebenfalls funktionieren. (Und sie führen zu einer kleineren und klareren Ausgabe.)

Ausführliche Informationen finden Sie im RFC .

¹JSON der entweichende auf JS gebaut, so verwendet sie \uXXXX, wo XXXXist ein UTF-16 - Code - Einheit. Für Codepunkte außerhalb des BMP bedeutet dies, dass Ersatzpaare codiert werden, die etwas haarig werden können. (Oder Sie können das Zeichen einfach direkt ausgeben, da JSON für Unicode-Text codiert ist und diese bestimmten Zeichen zulässt.)


Ist es in JSON wie in JavaScript gültig, Zeichenfolgen in doppelte oder einfache Anführungszeichen zu setzen? Oder ist es nur gültig, sie in doppelte Anführungszeichen zu setzen?
Behrang Saeedzadeh

14
Nur doppelte Anführungszeichen ( ").
Thanatos

3
@Sergei: Die Zeichen {[]}:?dürfen nicht mit einem einzigen Backslash maskiert werden. ( \:Ist beispielsweise in einer JSON-Zeichenfolge nicht gültig.) Alle diese Zeichen können optional mithilfe der \uXXXXSyntax maskiert werden, wobei mehrere Bytes verschwendet werden. Siehe §2.5 des RFC.
Thanatos

2
Ich bin mir nicht sicher, wie weit es unterstützt wird, aber meiner Erfahrung nach hat ein Aufruf, JSON.stringify()den Job zu erledigen.
LS

2
@BitTickler Ein Unicode-Zeichen ist überhaupt nicht vage - es bedeutet nur, dass es einen Codepunkt (oder Punkte) in der Unicode-Spezifikation hat. Wenn Sie std :: string verwenden, handelt es sich um eine Reihe von Unicode-Zeichen. Wenn Sie es serialisieren müssen, sagen wir zu einer Datei oder über das Netzwerk, kommt hier "welche Codierung" ins Spiel. Laut Thanatos möchten sie, dass Sie eine UTF verwenden, aber technisch kann jede Codierung verwendet werden, solange Es kann in Unicode-Zeichen rekonstituiert werden.
Gerard ONeill

54

Auszug aus 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
Nun, das war das OP-Tag
MonoThreaded

Verstehe nicht nur, wenn c <'', wechsle zu \ u. In meinem Fall gibt es das Zeichen \ uD38D, das 55357 und älter ist '', ändert sich also nicht zu \ u ...
Stony

1
@ Tony Klingt nach einer neuen Frage
MonoThreaded

@MonoThreaded Danke für deine Antwort, ich weiß immer noch nicht warum. aber schließlich habe ich die Methode geändert, um sie wie folgt zu beheben, wenn (c <'' || c> 0x7f) {t = "000" + Integer.toHexString (c) .toUpperCase (); sb.append ("\\ u" + t.substring (t.length () - 4)); } else {sb.append (c); }}
Stony

1
@Stony, alle anderen Zeichen als ", \ und Steuerzeichen (die vor "") sind in JSON-Zeichenfolgen gültig, solange die Ausgabecodierung übereinstimmt. Mit anderen Worten, Sie müssen "펍" nicht codieren, \uD38Dsolange die UTF-Codierung erhalten bleibt.
Meustrus

37

Versuchen Sie dies org.codehaus.jettison.json.JSONObject.quote("your string").

Laden Sie es hier herunter: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison


Auf jeden Fall die beste Lösung! Thx
Lastnico

aber dies zitiert keine Klammern wie [{
Sergei

1
@Sergei Sie müssen Klammern innerhalb einer JSON-Zeichenfolge nicht maskieren.
Yobert

Könnte nützlich sein, um zu zeigen, was dies tatsächlich zurückgibt.
Trevor

2
org.json.JSONObject.quote ("Ihre json Zeichenfolge") funktioniert auch gut
Webjockey

23

org.json.simple.JSONObject.escape () maskiert Anführungszeichen, \, /, \ r, \ n, \ b, \ f, \ t und andere Steuerzeichen. Es kann verwendet werden, um JavaScript-Codes zu umgehen.

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

3
Es hängt von der von Ihnen verwendeten JSON-Bibliothek ab (JSONObject.escape, JSONObject.quote, ..), aber es ist immer eine statische Methode, die den Angebotsjob ausführt, und sollte einfach wiederverwendet werden
amine

Zu welcher Bibliothek gehört org.json? Ich habe es nicht auf meinem Klassenweg.
Alex Spurling


22

Apache Commons Lang unterstützt dies jetzt. Stellen Sie einfach sicher, dass Sie eine ausreichend aktuelle Version von Apache commons lang in Ihrem Klassenpfad haben. Sie benötigen Version 3.2+

Versionshinweise für Version 3.2

LANG-797: Escape / unescapeJson zu StringEscapeUtils hinzugefügt.


Dies ist die praktischste Antwort für mich. Die meisten Projekte verwenden bereits apache commons lang, sodass keine Abhängigkeit für eine Funktion hinzugefügt werden muss. Ein JSON-Builder wäre wahrscheinlich die beste Antwort.
Absmiths

Als Folge habe ich javax.json.JsonObjectBuilder und javax.json.JsonWriter gefunden, da ich nicht herausfinden kann, wie ein Kommentar bearbeitet werden soll. Ich habe einen neuen hinzugefügt. Sehr schöne Builder / Writer-Kombination.
Absmiths

1
Dies ist in Apache Commons Lang veraltet. Sie müssen Apache Commons Text verwenden . Leider folgt diese Bibliothek der optionalen / veralteten Spezifikation, indem /Zeichen maskiert werden . Dies zerstört viele Dinge, einschließlich JSON mit URLs. Der ursprüngliche Vorschlag hatte /als Sonderzeichen zu entkommen, aber dies ist nicht mehr der Fall, wie wir in der neuesten Spezifikation zum Zeitpunkt des Schreibens sehen können
Adamnfish

10

org.json.JSONObject quote(String data) Methode macht den Job

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

Auszug aus der Dokumentation:

Codiert Daten als JSON-Zeichenfolge. Dies gilt für Anführungszeichen und alle erforderlichen Zeichen, die entkommen . [...] Null wird als leere Zeichenfolge interpretiert


1
org.apache.sling.commons.json.JSONObjecthat auch das gleiche
Jordan Shurmer

5

StringEscapeUtils.escapeJavaScriptIch StringEscapeUtils.escapeEcmaScriptsollte den Trick auch machen.


10
escapeJavaScriptentgeht einfachen Anführungszeichen als \', was falsch ist.
Laurt

4

Wenn Sie fastexml jackson verwenden, können Sie Folgendes verwenden: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

Wenn Sie Codehaus Jackson verwenden, können Sie Folgendes verwenden: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


3

Sie sind sich nicht sicher, was Sie unter "manuell erstellen von json" verstehen, aber Sie können etwas wie gson ( http://code.google.com/p/google-gson/ ) verwenden, das Ihre HashMap, Ihr Array, Ihren String usw. Transformieren würde auf einen JSON-Wert. Ich empfehle dafür einen Rahmen.


2
Mit manuell meinte ich nicht die Verwendung einer JSON-Bibliothek wie Simple JSON, Gson oder XStream.
Behrang Saeedzadeh

Nur eine Frage der Neugier - warum sollten Sie keine dieser APIs verwenden? Es ist, als würde man versuchen, URLs manuell zu umgehen, anstatt URLEncode / Decode zu verwenden ...
Vladimir

1
Nicht wirklich die gleichen, kommen diese Bibliotheken mit viel mehr als das Äquivalent von URLEncode / Decode, sie umfassen eine ganze Serialisierung Paket Persistenz von Java - Objekt in json Form zu ermöglichen, und manchmal Ihr wirklich nur eine kurze Reihe von Text kodieren müssen
jmd

2
Eine manuelle Erstellung von JSON ist sinnvoll, wenn Sie keine Bibliothek nur zum Serialisieren kleiner Datenmengen
hinzufügen

2
Ich würde darum bitten, dass ein Teammitglied aus jedem Projekt entfernt wird, an dem ich beteiligt bin, wenn es sich traut, JSON manuell zu erstellen, wenn es dafür eine qualitativ hochwertige Bibliothek gibt.
Michael Joyce

2

Ich habe nicht die Zeit aufgewendet, um 100% sicher zu stellen, aber es hat für meine Eingaben genug funktioniert, um von Online-JSON-Validatoren akzeptiert zu werden:

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

obwohl es nicht besser aussieht als org.codehaus.jettison.json.JSONObject.quote("your string")

Ich verwende in meinem Projekt einfach bereits Geschwindigkeitswerkzeuge - mein "manuelles JSON" -Gebäude befand sich in einer Geschwindigkeitsvorlage


2

Für diejenigen, die hierher gekommen sind, um nach einer Befehlszeilenlösung zu suchen, wie ich, funktioniert der --data-urlencode von cURL einwandfrei:

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

sendet

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

, zum Beispiel. Größere JSON-Daten können in eine Datei eingefügt werden, und Sie würden die @ -Syntax verwenden, um eine Datei anzugeben, aus der die zu maskierenden Daten verschlungen werden sollen. Zum Beispiel wenn

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

du würdest verwenden

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

Und jetzt ist dies auch ein Tutorial, wie man Freebase über die Kommandozeile abfragt :-)


2

Verwenden Sie die EscapeUtils-Klasse in der Commons-Lang-API.

EscapeUtils.escapeJavaScript("Your JSON string");

1
Beachten Sie, dass einfache Anführungszeichen beispielsweise anders behandelt werden, wenn Sie zu Javascript oder JSON wechseln. In commons.lang 3.4 hat StringEscapeUtils ( commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/… ) eine EscapeJSON-Methode, die sich von der EscapeJavaScript-Methode in commons.lang 2: commons.apache unterscheidet. org / richtig / commons-lang / javadocs / api-2.6 / org /…
GlennV

1

Betrachten Sie Moshi ‚s JsonWriter Klasse. Es hat eine wunderbare API und reduziert das Kopieren auf ein Minimum. Alles kann gut in eine Datei, einen OutputStream usw. gestreamt werden.

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

Wenn Sie die Zeichenfolge in der Hand haben möchten:

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


0

Wenn Sie JSON innerhalb der JSON-Zeichenfolge maskieren müssen, scheint die Verwendung von org.json.JSONObject.quote ("Ihre json-Zeichenfolge, die maskiert werden muss") gut zu funktionieren


0

Mit der \ uXXXX-Syntax kann dieses Problem gelöst werden. Google UTF-16 mit dem Namen des Zeichens. Sie können XXXX herausfinden, zum Beispiel: utf-16 doppeltes Anführungszeichen


0

Die Methoden hier, die die tatsächliche Implementierung zeigen, sind alle fehlerhaft.
Ich habe keinen Java-Code, aber nur für den Datensatz können Sie diesen C # -Code leicht konvertieren:

Mit freundlicher Genehmigung des Monoprojekts @ 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();
}

Dies kann verdichtet werden

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

Wie ist die quote()in anderen Antworten beschriebene Methode fehlerhaft?
Sandy

0

Ich denke, die beste Antwort im Jahr 2017 ist die Verwendung der javax.json-APIs. Verwenden Sie javax.json.JsonBuilderFactory, um Ihre json-Objekte zu erstellen, und schreiben Sie die Objekte dann mit javax.json.JsonWriterFactory aus. Sehr schöne Builder / Writer-Kombination.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.