Bagaimana cara saya melepaskan diri dari string di JSON?

154

Saat membuat data JSON secara manual, bagaimana cara saya keluar dari bidang string? Haruskah saya menggunakan sesuatu seperti Apache Commons Lang StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmlatau harus saya gunakan java.net.URLEncoder?

Masalahnya adalah bahwa ketika saya menggunakan SEU.escapeHtml, itu tidak lepas tanda kutip dan ketika saya membungkus seluruh string dalam sepasang 's, JSON yang salah bentuk akan dihasilkan.

Behrang Saeedzadeh
sumber
20
Jika Anda membungkus seluruh string menjadi sepasang ', Anda akan ditakdirkan sejak awal: String JSON hanya dapat dikelilingi dengan ". Lihat ietf.org/rfc/rfc4627.txt .
Thanatos
2
+1 untuk StringEscapeUtilitiesgaris besarnya. Ini sangat berguna.
Muhammad Gelbana

Jawaban:

157

Idealnya, temukan pustaka JSON dalam bahasa Anda yang dapat Anda beri makan beberapa struktur data yang sesuai, dan biarkan khawatir tentang cara melarikan diri . Itu akan membuat Anda lebih waras. Jika karena alasan apa pun Anda tidak memiliki perpustakaan dalam bahasa Anda, Anda tidak ingin menggunakannya (saya tidak menyarankan ini¹), atau Anda sedang menulis perpustakaan JSON, baca terus.

Hindarilah menurut RFC. JSON cukup liberal: Satu-satunya karakter Anda harus melarikan diri adalah \, ", dan kode kontrol (sesuatu yang kurang dari U + 0020).

Struktur pelarian ini khusus untuk JSON. Anda membutuhkan fungsi spesifik JSON. Semua lolos dapat ditulis sebagai di \uXXXXmana XXXXunit kode UTF-16¹ untuk karakter itu. Ada beberapa cara pintas, seperti \\, yang berfungsi juga. (Dan mereka menghasilkan output yang lebih kecil dan lebih jelas.)

Untuk detail lengkap, lihat RFC .

Pelarian SONJSON dibangun di atas JS, jadi ia menggunakan \uXXXX, di mana XXXXadalah unit kode UTF-16. Untuk titik kode di luar BMP, ini berarti menyandikan pasangan pengganti, yang bisa menjadi sedikit berbulu. (Atau, Anda bisa menampilkan karakter secara langsung, karena JSON yang dikodekan adalah teks Unicode, dan memungkinkan karakter khusus ini.)

Thanatos
sumber
Apakah valid dalam JSON, seperti dalam JavaScript, untuk menyertakan string dalam tanda kutip ganda atau tanda kutip tunggal? Atau apakah itu hanya berlaku untuk menyertakannya dalam tanda kutip ganda?
Behrang Saeedzadeh
14
Hanya tanda kutip ganda ( ").
Thanatos
3
@Sergei: Karakter {[]}:?tidak boleh lolos dengan backslash tunggal. ( \:, misalnya, tidak valid dalam string JSON.) Semua itu secara opsional dapat diloloskan menggunakan \uXXXXsintaks, dengan membuang beberapa byte. Lihat §2.5 dari RFC.
Thanatos
2
Saya tidak yakin seberapa luas itu didukung, tetapi dalam pengalaman saya panggilan untuk JSON.stringify()melakukan pekerjaan itu.
LS
2
@ BitTickler karakter unicode tidak samar sama sekali - itu hanya berarti bahwa ia memiliki titik kode (atau poin) dalam spesifikasi unicode. Ketika Anda menggunakan std :: string, itu adalah sekelompok karakter unicode. Ketika Anda perlu membuat cerita bersambung, katakanlah ke file atau di seluruh jaringan, di situlah 'encoding mana' masuk. Tampaknya menurut Thanatos bahwa mereka ingin Anda menggunakan UTF, tetapi secara teknis penyandian apa pun dapat digunakan selama itu dapat disusun kembali menjadi karakter unicode.
Gerard ONeill
54

Ekstrak dari 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();
     }
MonoThreaded
sumber
10
Nah, ini adalah tag OP
MonoThreaded
Jangan mengerti hanya ketika c <'', ubah ke \ u. Dalam kasus saya, ada karakter \ uD38D, yaitu 55357 dan lebih '', jadi tidak berubah menjadi \ u ...
Stony
1
@Stony Kedengarannya seperti pertanyaan baru
MonoThreaded
@MonoThreaded Terima kasih atas balasan Anda, saya masih tidak tahu mengapa. tetapi akhirnya, saya mengubah metode untuk memperbaikinya seperti di bawah ini, 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, semua karakter selain ",, \ dan karakter kontrol (yang sebelum "") valid di dalam string JSON selama encoding output cocok. Dengan kata lain, Anda tidak perlu menyandikan "펍" \uD38Dselama pengkodean UTF dipertahankan.
meustrus
37

Coba ini org.codehaus.jettison.json.JSONObject.quote("your string").

Unduh di sini: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison

dpetruha
sumber
Pasti solusi terbaik! Thx
Lastnico
tetapi ini tidak mengutip dari kawat gigi seperti [{
Sergei
1
@Sergei Anda tidak perlu melarikan diri kawat gigi di dalam string JSON.
Yobert
Mungkin bermanfaat untuk menunjukkan apa yang sebenarnya dikembalikan.
Trevor
2
org.json.JSONObject.quote ("your json string") juga berfungsi dengan baik
webjockey
23

org.json.simple.JSONObject.escape () lolos dari tanda kutip, \, /, \ r, \ n, \ b, \ f, \ t dan karakter kontrol lainnya. Itu dapat digunakan untuk menghindari kode JavaScript.

import org.json.simple.JSONObject;
String test =  JSONObject.escape("your string");
Dan-Dev
sumber
3
Itu tergantung pada pustaka json yang Anda gunakan (JSONObject.escape, JSONObject.quote, ..) tetapi itu selalu merupakan metode statis yang melakukan pekerjaan mengutip dan hanya harus digunakan kembali
amine
Perpustakaan mana yang merupakan bagian dari org.json? Saya tidak memilikinya di kelas saya.
Alex Spurling
22

Apache commons lang sekarang mendukung ini. Pastikan Anda memiliki versi Apache commons yang cukup baru di classpath Anda. Anda membutuhkan versi 3.2+

Catatan Rilis untuk versi 3.2

LANG-797: Menambahkan escape / unescapeJson ke StringEscapeUtils.

NS du Toit
sumber
Ini jawaban paling praktis untuk saya. Sebagian besar proyek sudah menggunakan apache commons lang, jadi tidak perlu menambahkan ketergantungan untuk satu fungsi. Pembangun JSON mungkin akan menjadi jawaban terbaik.
absmiths
Sebagai tindak lanjut, dan karena saya tidak tahu cara mengedit komentar, saya menambahkan yang baru, saya menemukan javax.json.JsonObjectBuilder dan javax.json.JsonWriter. Kombinasi pembangun / penulis yang sangat bagus.
absmiths
1
Ini tidak digunakan lagi di apache commons, Anda perlu menggunakan teks apache commons . Sayangnya, perpustakaan ini mengikuti spesifikasi opsional / usang dengan melarikan diri /karakter. Ini memecah banyak hal termasuk JSON dengan URL di dalamnya. Proposal asli memiliki /sebagai char khusus untuk melarikan diri tetapi ini tidak lagi terjadi, seperti yang dapat kita lihat dalam spesifikasi terbaru pada saat penulisan
adamnfish
10

org.json.JSONObject quote(String data) Metode melakukan pekerjaan

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

Ambil dari dokumentasi:

Mengkodekan data sebagai string JSON. Ini berlaku kutipan dan setiap karakter yang diperlukan melarikan diri . [...] Null akan diartikan sebagai string kosong

IG Pascual
sumber
1
org.apache.sling.commons.json.JSONObjectjuga memiliki hal yang sama
Jordan Shurmer
5

StringEscapeUtils.escapeJavaScriptSaya StringEscapeUtils.escapeEcmaScriptharus melakukan triknya juga.

Hanubindh Krishna
sumber
10
escapeJavaScriptlolos dari tanda kutip tunggal sebagai \', yang tidak benar.
laurt
4

Jika Anda menggunakan fastexml jackson, Anda dapat menggunakan yang berikut ini: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

Jika Anda menggunakan codehaus jackson, Anda dapat menggunakan yang berikut ini: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)

Dhiraj
sumber
3

Tidak yakin apa yang Anda maksud dengan "membuat json secara manual", tetapi Anda dapat menggunakan sesuatu seperti gson ( http://code.google.com/p/google-gson/ ), dan itu akan mengubah HashMap, Array, String, dll. , ke nilai JSON. Saya sarankan pergi dengan kerangka kerja untuk ini.

Vladimir
sumber
2
Secara manual saya maksud bukan dengan menggunakan perpustakaan JSON seperti Simple JSON, Gson, atau XStream.
Behrang Saeedzadeh
Hanya masalah rasa ingin tahu - mengapa Anda tidak ingin menggunakan salah satu dari API ini? Ini seperti mencoba melepaskan URL secara manual, alih-alih menggunakan URLEncode / Decode ...
Vladimir
1
Tidak juga sama, pustaka-pustaka itu datang dengan lebih banyak daripada yang setara dengan URLEncode / Decode, mereka menyertakan paket serialisasi keseluruhan untuk memungkinkan persistensi objek java dalam bentuk json, dan kadang-kadang Anda benar-benar hanya perlu menyandikan sekumpulan teks pendek
jmd
2
lakukan pembuatan manual JSON secara masuk akal, jika Anda tidak ingin menyertakan perpustakaan hanya untuk membuat serial data
Aditya Kumar Pandey
2
Saya akan meminta agar anggota tim dihapus dari proyek apa pun yang saya ikuti jika mereka berani membuat JSON secara manual di mana ada perpustakaan berkualitas tinggi untuk melakukannya.
Michael Joyce
2

Saya belum menghabiskan waktu untuk memastikan 100%, tetapi itu bekerja untuk masukan saya cukup untuk diterima oleh validator JSON online:

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

meskipun tidak terlihat lebih baik daripada org.codehaus.jettison.json.JSONObject.quote("your string")

Saya cukup menggunakan alat kecepatan dalam proyek saya - bangunan "manual JSON" saya berada dalam templat kecepatan

Tjunkie
sumber
2

Bagi mereka yang datang ke sini mencari solusi baris perintah, seperti saya, --Data-urlencode cURL berfungsi dengan baik:

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

mengirim

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

, sebagai contoh. Data JSON yang lebih besar dapat dimasukkan ke dalam file dan Anda akan menggunakan sintaks @ untuk menentukan file yang akan disedot dari data yang akan diloloskan. Misalnya, jika

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

Anda akan menggunakan

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

Dan sekarang, ini juga merupakan tutorial tentang cara meminta Freebase dari baris perintah :-)

vijucat
sumber
2

Gunakan kelas EscapeUtils di commons lang API.

EscapeUtils.escapeJavaScript("Your JSON string");
theJ
sumber
1
Perhatikan bahwa kutipan tunggal misalnya ditangani secara berbeda ketika melarikan diri ke javascript atau json. Dalam commons.lang 3.4 StringEscapeUtils ( commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/… ) memiliki metode escapeJSON yang berbeda dari metode escapeJavaScript di commons.lang 2: commons.apache. org / proper / commons-lang / javadocs / api-2.6 / org / ...
GlennV
1

Pertimbangkan Moshi 's JsonWriter kelas. Ini memiliki API yang luar biasa dan mengurangi penyalinan seminimal mungkin, semuanya dapat dialirkan dengan baik ke arsip, OutputStream, dll.

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

Jika Anda ingin string di tangan:

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

Jika Anda perlu keluar dari JSON di dalam string JSON, gunakan org.json.JSONObject.quote ("string json Anda yang perlu diloloskan") tampaknya berfungsi dengan baik

webjockey
sumber
0

menggunakan sintaks \ uXXXX dapat menyelesaikan masalah ini, google UTF-16 dengan nama tanda, Anda dapat mengetahui XXXX, misalnya: utf-16 double quote

David
sumber
0

Metode di sini yang menunjukkan implementasi aktual semuanya salah.
Saya tidak memiliki kode Java, tetapi hanya sebagai catatan, Anda dapat dengan mudah mengkonversi kode C # ini:

Atas perkenan mono-proyek @ 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();
}

Ini bisa dipadatkan

    // 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();
    }
}
Stefan Steiger
sumber
Bagaimana quote()metode yang dijelaskan dalam jawaban lain salah?
Sandy
0

Saya pikir jawaban terbaik di 2017 adalah menggunakan API javax.json. Gunakan javax.json.JsonBuilderFactory untuk membuat objek json Anda, kemudian tulis objek menggunakan javax.json.JsonWriterFactory. Kombinasi pembangun / penulis yang sangat bagus.

pengacau
sumber