JSON'da dizelerden nasıl kaçmalıyım?


154

Manuel olarak JSON verileri oluştururken, dize alanlarından nasıl çıkmalıyım? Apache Commons Lang en gibi Meli ben kullanım şey StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmlya kullanmalıyım java.net.URLEncoder?

Sorun, kullandığımda SEU.escapeHtml, tırnaklardan kaçmıyor ve tüm dizeyi bir çift olarak 'sardığımda, hatalı biçimlendirilmiş bir JSON oluşturulacak.


20
Tüm dizeyi bir çift olarak 'sarıyorsanız, baştan mahkum olursunuz: JSON dizeleri yalnızca çevrelenebilir ". Bkz. İetf.org/rfc/rfc4627.txt .
Thanatos

2
StringEscapeUtilitiesAnahat için +1 . Oldukça kullanışlı.
Muhammed Gelbana

Yanıtlar:


157

İdeal olarak, dilinizde bazı uygun veri yapılarını besleyebileceğiniz bir JSON kütüphanesi bulun ve işlerden nasıl kaçacağınız konusunda endişelenmesine izin verin . Seni daha aklı tutacak. Herhangi bir nedenle dilinizde bir kitaplığınız yoksa, bir tane kullanmak istemezsiniz (bunu tavsiye etmem¹¹) veya bir JSON kitaplığı yazıyorsanız, okumaya devam edin.

RFC'ye göre kaçın. JSON oldukça liberaldir: Yalnızca karakterler gerekir kaçış vardır \, "kontrol kodları (şey daha az U + 0020 den) ve.

Bu kaçış yapısı JSON'a özgüdür. JSON'a özgü bir işleve ihtiyacınız vardır. Tüm çıkış karakterleri, bu karakter için UTF-16 kod birimi¹ \uXXXXolduğu gibi yazılabilir XXXX. Bunun gibi çalışan birkaç kısayol vardır \\. (Ve daha küçük ve daha net bir çıktı sağlarlar.)

Tüm ayrıntılar için RFC'ye bakın .

¹JSON en kaçış yüzden kullanır, JS üzerine inşa edilmiştir \uXXXXnerede, XXXXUTF-16 kod birimidir. BMP dışındaki kod noktaları için bu, biraz kıllı olabilen vekil çiftleri kodlamak anlamına gelir. (Veya JSON için kodlanmış Unicode metin olduğundan ve bu belirli karakterlere izin verdiğinden, karakteri doğrudan çıktılayabilirsiniz.)


JSON'da, JavaScript'te olduğu gibi, dizeleri çift tırnak veya tek tırnak içine almak geçerli midir? Yoksa sadece çift tırnak içine almak geçerli midir?
Behrang Saeedzadeh

14
Sadece çift tırnak işareti ( ").
Thanatos

3
@Sergei: Karakterler {[]}:?tek bir ters eğik çizgi ile kaçmamalıdır . ( \:örneğin, bir JSON dizesinde geçerli değildir.) Bunların tümü isteğe bağlı olarak \uXXXXbirkaç baytın israfında sözdizimi kullanılarak kaçabilir. Bkz. RFC §2.5.
Thanatos

2
Ne kadar geniş bir şekilde desteklendiğinden emin değilim, ancak tecrübelerime göre JSON.stringify()bu işi yapma çağrısı .
LS

2
@BitTickler bir unicode karakteri hiç de belirsiz değildir - sadece unicode spesifikasyonunda bir kod noktası (veya noktaları) olduğu anlamına gelir. Std :: string kullandığınızda, bu bir grup unicode karakterdir. Serileştirmeniz gerektiğinde, bir dosyaya veya ağ genelinde diyelim, 'hangi kodlamanın' devreye girdiği yer burasıdır. Thanatos'a göre bir UTF kullanmanızı istiyorlar, ancak teknik olarak herhangi bir kodlama kullanılabilir olduğu sürece unicode karakterler olarak yeniden oluşturulabilir.
Gerard ONeill

54

Gönderen Özü 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
OP etiketi
buydu

Sadece c <'' olduğunda anlamayın, \ u olarak değiştirin. Benim durumumda, 55357 ve üzeri olan 'uD38D karakteri var, bu yüzden \ u ...' ya değişmiyor
Stony

1
@Stony Yeni bir soru gibi geliyor
MonoThreaded

@MonoThreaded Cevabınız için teşekkürler, hala nedenini bilmiyorum. ama son olarak, aşağıdaki gibi düzeltmek için yöntemi değiştirdim, eğer (c <'' || c> 0x7f) {t = "000" + Integer.toHexString (c) .toUpperCase (); sb.append ("\\ u" + t.substring (t.length () - 4)); } başka {sb.append (c); }}
Taşlı

1
@Stony, dışındaki tüm karakterler ", \ ve kontrol karakterleri ( “” önce olanlar) uzun çıkış kodlayan olarak maçları geçerli içeride JSON dizelerdir. Başka bir deyişle, \uD38DUTF kodlaması korunduğu sürece “펍” kodlamanız gerekmez .
meustrus

37

Bunu dene org.codehaus.jettison.json.JSONObject.quote("your string").

Buradan indirin: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison


Kesinlikle en iyi çözüm! Thx
Lastnico

ancak bu, {{
Sergei

1
@Sergei Bir JSON dizesinin içindeki parantezlerden kaçmak zorunda değilsiniz.
Yobert

Bunun gerçekte ne döndürdüğünü göstermek için yararlı olabilir.
Trevor

2
org.json.JSONObject.quote ("json dizeniz") de iyi çalışıyor
webjockey

23

org.json.simple.JSONObject.escape () tırnak, \, /, \ r, \ n, \ b, \ f, \ t ve diğer kontrol karakterlerinden kaçar. JavaScript kodlarından kaçmak için kullanılabilir.

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

3
Kullandığınız json kütüphanesine (JSONObject.escape, JSONObject.quote, ..) bağlıdır, ancak her zaman alıntı işini yapan statik bir yöntemdir ve sadece yeniden kullanılmalıdır
amine

Hangi kütüphane org.json parçasıdır? Sınıf yolumda yok.
Alex Spurling


22

Apache commons lang artık bunu destekliyor. Sadece sınıf yolunuzda Apache müşterek dilinin yeterince yeni bir sürümüne sahip olduğunuzdan emin olun. 3.2 ve üzeri sürümlere ihtiyacınız olacak

Sürüm 3.2 için Sürüm Notları

LANG-797: StringEscapeUtils'e escape / unescapeJson eklendi.


Bu benim için en pratik cevap. Çoğu proje zaten apache commons lang kullanıyor, bu nedenle bir işlev için bağımlılık eklemenize gerek yok. Bir JSON üreticisi muhtemelen en iyi yanıt olacaktır.
Absmiths

Bir takip olarak ve bir yorumu nasıl düzenleyeceğimi anlayamadığım için yeni bir tane ekledim, javax.json.JsonObjectBuilder ve javax.json.JsonWriter buldum. Çok güzel oluşturucu / yazar kombinasyonu.
Absmiths

1
Bu apache commons lang'de kullanımdan kaldırılmıştır, apache commons metnini kullanmanız gerekir . Ne yazık ki, bu kütüphane /karakterlerden kaçarak isteğe bağlı / güncel olmayan özellikleri izler . Bu, içinde URL'ler bulunan JSON dahil olmak üzere birçok şeyi kırar. Orijinal öneri /kaçmak için özel bir karakter olarak vardı , ancak bu yazım sırasında en son spesifikasyonda
adamnfish

10

org.json.JSONObject quote(String data) yöntem işi yapar

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

Belgelerden alıntı:

Verileri JSON dizesi olarak kodlar. Bu, tırnak işaretleri ve kaçan tüm karakterleri uygular . [...] Boş, boş bir dize olarak yorumlanacak


1
org.apache.sling.commons.json.JSONObjectAynı şey
Ürdün Shurmer

5

StringEscapeUtils.escapeJavaScript/ StringEscapeUtils.escapeEcmaScriptÇok hile yapmak gerekir.


10
escapeJavaScript\'yanlış olarak tek tırnak işareti kaçar .
laurt

4

Fastexml jackson kullanıyorsanız, aşağıdakileri kullanabilirsiniz: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

Codehaus jackson kullanıyorsanız, aşağıdakileri kullanabilirsiniz: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)


3

"Manuel olarak json oluşturarak" ile ne demek istediğinizden emin değilsiniz, ancak gson ( http://code.google.com/p/google-gson/ ) gibi bir şey kullanabilirsiniz ve bu da HashMap, Array, String vb. , bir JSON değerine ayarlayın. Bunun için bir çerçeve ile gitmenizi tavsiye ederim.


2
Manuel olarak Basit JSON, Gson veya XStream gibi bir JSON kütüphanesi kullanmak istemedim.
Behrang Saeedzadeh

Sadece bir merak meselesi - neden bu API'lardan birini kullanmak istemeyesiniz? URL'leri URLEncode / Decode kullanmak yerine manuel olarak kaçmaya çalışmak gibi ...
Vladimir

1
Gerçekten aynı değil, bu kütüphaneler URLEncode / Decode eşdeğerinden çok daha fazlası ile birlikte gelir, java formunun json biçiminde kalıcılığını sağlamak için tam bir serileştirme paketi içerir ve bazen gerçekten sadece kısa bir metin grubunu kodlamanız gerekir
jmd

2
sadece küçük veri parçalarını serileştirmek için bir kütüphane eklemek istemiyorsanız, JSON'un manuel olarak oluşturulması mantıklıdır
Aditya Kumar Pandey

2
Bir ekip üyesinin JSON'u yüksek kaliteli bir kütüphanenin olduğu yerde el ile oluşturmaya cesaret ederse, çalıştığım herhangi bir projeden kaldırılmasını rica ediyorum.
Michael Joyce

2

% 100 kesinleştirmek için zaman harcamadım, ancak girişlerimin çevrimiçi JSON doğrulayıcıları tarafından kabul edilmek için yeterince çalıştı:

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

daha iyi görünmese de org.codehaus.jettison.json.JSONObject.quote("your string")

Projemde zaten hız araçlarını kullanıyorum - "manuel JSON" binam bir hız şablonundaydı


2

Buraya benim gibi bir komut satırı çözümü arayanlar için cURL'nin --data-urlencode'u iyi çalışıyor:

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

gönderir

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

, Örneğin. Daha büyük JSON verileri bir dosyaya yerleştirilebilir ve kaçacak verilerden ayrıştırılacak bir dosya belirtmek için @ sözdizimini kullanırsınız. Örneğin,

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

kullanırdın

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

Ve şimdi, bu aynı zamanda komut satırından Freebase'i nasıl sorgulayacağınıza dair bir öğretici :-)



1

Moshi'yi düşünün 'ın JsonWriter sınıfını. Harika bir API'sı vardır ve kopyalamayı en aza indirir, her şey bir dosyaya, OutputStream'e vb.

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

Dizeyi elde etmek istiyorsanız:

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


0

JSON dizesinin içindeki JSON'dan kaçmanız gerekiyorsa, org.json.JSONObject.quote ("kaçması gereken json dizeniz" kullanın) iyi çalışıyor gibi görünüyor


0

\ uXXXX sözdizimini kullanarak bu sorunu çözebilir, google UTF-16 işareti adıyla, XXXX'i bulabilirsiniz, örneğin: utf-16 çift tırnak


0

Buradaki asıl uygulamayı gösteren yöntemlerin hepsi hatalı.
Java kodum yok, ama sadece kayıt için kolayca bu C # kodunu dönüştürebilirsiniz:

Mono projenin izniyle @ 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();
}

Bu içine sıkıştırılabilir

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

quote()Diğer cevaplarda açıklanan yöntem nasıl hatalı?
Sandy

0

2017'de en iyi yanıtın javax.json API'lerini kullanmak olduğunu düşünüyorum. Json nesnelerinizi oluşturmak için javax.json.JsonBuilderFactory kullanın, sonra javax.json.JsonWriterFactory kullanarak nesneleri yazın. Çok güzel oluşturucu / yazar kombinasyonu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.