Como devo escapar de strings no JSON?

154

Ao criar dados JSON manualmente, como devo escapar dos campos de sequência? Devo usar algo como Apache Commons Lang StringEscapeUtilities.escapeHtml, StringEscapeUtilities.escapeXmlou eu deveria usarjava.net.URLEncoder ?

O problema é que, quando eu uso SEU.escapeHtml, ele não escapa aspas e quando envolvo toda a string em um par de 's, um JSON malformado será gerado.

Behrang Saeedzadeh
fonte
20
Se você estiver agrupando a cadeia inteira em um par de ', estará condenado desde o início: as cadeias JSON só podem ser cercadas ". Veja ietf.org/rfc/rfc4627.txt .
Thanatos
2
+1 para o StringEscapeUtilitiesesboço. É bastante útil.
Muhammad Gelbana 5/11

Respostas:

157

Idealmente, encontre uma biblioteca JSON no seu idioma na qual você possa alimentar alguma estrutura de dados apropriada e deixe-a se preocupar com como escapar de coisas . Vai mantê-lo muito mais saudável. Se, por qualquer motivo, você não tiver uma biblioteca no seu idioma, não quiser usá-la (eu não sugeriria isso¹) ou estiver escrevendo uma biblioteca JSON, continue lendo.

Escape de acordo com a RFC. JSON é muito liberal: Os únicos caracteres que você deve escapar são \, "e códigos de controle (nada menos do que U + 0020).

Essa estrutura de escape é específica para JSON. Você precisará de uma função específica JSON. Todas as fugas podem ser escritas como \uXXXXonde XXXXestá a unidade de código UTF-16¹ para esse caractere. Existem alguns atalhos, como \\, que também funcionam. (E eles resultam em uma saída menor e mais clara.)

Para detalhes completos, consulte o RFC .

O escape de JSON é construído em JS, então ele usa \uXXXX, onde XXXXestá uma unidade de código UTF-16. Para pontos de código fora do BMP, isso significa codificar pares substitutos, o que pode ser um pouco complicado. (Ou, você pode simplesmente gerar o caractere diretamente, pois o JSON codificado para é texto Unicode e permite esses caracteres específicos.)

Thanatos
fonte
É válido no JSON, como no JavaScript, colocar cadeias de caracteres entre aspas duplas ou aspas simples? Ou é válido apenas colocá-los entre aspas duplas?
Behrang Saeedzadeh
14
Apenas aspas duplas ( ").
Thanatos
3
@ Emerge: Os caracteres {[]}:?não devem ser escapados com uma única barra invertida. ( \:por exemplo, não é válido em uma cadeia de caracteres JSON.) Todos esses itens podem opcionalmente ser escapados usando a \uXXXXsintaxe, com desperdício de vários bytes. Veja §2.5 da RFC.
Thanatos
2
Não tenho certeza de quão amplamente ele é suportado, mas, na minha experiência, um chamado para JSON.stringify()fazer o trabalho.
LS
2
@BitTickler, um caractere unicode não é vago - significa apenas que ele tem um ponto de código (ou pontos) na especificação unicode. Quando você usa std :: string, há vários caracteres unicode. Quando você precisar serializá-lo, digamos em um arquivo ou na rede, é aí que entra 'qual codificação'. Parece que, segundo Thanatos, eles querem que você use um UTF, mas tecnicamente qualquer codificação pode ser usada desde que pode ser reconstituído em caracteres unicode.
precisa
54

Extrato do 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
fonte
10
Bem, esta foi a tag OP
MonoThreaded
Não entenda apenas quando c <'', altere para \ u. No meu caso, não há caráter \ uD38D, que é 55357 e over '', então não muda para \ u ...
Stony
1
@Stony Soa como uma nova pergunta
MonoThreaded
@MonoThreaded Obrigado pela sua resposta, ainda não sei por que. mas, finalmente, mudei o método para corrigi-lo como abaixo: 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, todos os outros do que os caracteres ", \ , e caracteres de controlo (aqueles antes de “”) são cadeias dentro JSON válidos como partidas desde que a saída que codificam. Em outras palavras, você não precisa codificar “펍” \uD38Denquanto a codificação UTF for preservada.
precisa saber é
37

Tente isso org.codehaus.jettison.json.JSONObject.quote("your string").

Faça o download aqui: http://mvnrepository.com/artifact/org.codehaus.jettison/jettison

dpetruha
fonte
Definitivamente a melhor solução! Thx
Lastnico
mas isso não cita chaves como [{
Sergei
1
@Sergei Você não precisa escapar de chaves dentro de uma string JSON.
Yobert 02/02
Pode ser útil mostrar o que isso realmente retorna.
Trevor
2
org.json.JSONObject.quote ("your json string") também funciona bem
webjockey 16/16
23

org.json.simple.JSONObject.escape () escapa aspas, \, /, \ r, \ n, \ b, \ f, \ te outros caracteres de controle. Pode ser usado para escapar dos códigos JavaScript.

import org.json.simple.JSONObject;
String test =  JSONObject.escape("your string");
Dan-Dev
fonte
3
Depende da biblioteca JSON você está usando (JSONObject.escape, JSONObject.quote, ..), mas é sempre um método estático fazendo o trabalho de citar e simplesmente devem ser reutilizados
amina
De que biblioteca faz parte o org.json? Eu não tenho isso no meu caminho de classe.
Alex Spurling
22

O Apache commons lang agora suporta isso. Apenas verifique se você tem uma versão recente o suficiente do Apache commons em seu caminho de classe. Você precisará da versão 3.2 ou superior

Notas de versão da versão 3.2

LANG-797: Adicionado escape / unescapeJson no StringEscapeUtils.

NS du Toit
fonte
Esta é a resposta mais prática para mim. A maioria dos projetos já usa o apache commons lang, portanto, não há necessidade de adicionar uma dependência para uma função. Um construtor JSON provavelmente seria a melhor resposta.
absmiths
Como acompanhamento, e como não consigo descobrir como editar um comentário, adicionei um novo, encontrei javax.json.JsonObjectBuilder e javax.json.JsonWriter. Muito boa combinação de construtor / escritor.
absmiths
1
Isso foi preterido no apache commons lang, você precisa usar o texto do apache commons . Infelizmente, esta biblioteca segue as especificações opcionais / desatualizadas, escapando /caracteres. Isso quebra muitas coisas, incluindo JSON com URLs. A proposta original tinha /como um char especial para escapar, mas isso não é mais o caso, como podemos ver na mais recente especificação no momento da escrita
adamnfish
10

org.json.JSONObject quote(String data) método faz o trabalho

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

Extrair da documentação:

Codifica dados como uma sequência JSON. Isso aplica aspas e qualquer caractere necessário escapando . [...] nulo será interpretado como uma string vazia

IG Pascual
fonte
1
org.apache.sling.commons.json.JSONObjecttambém tem a mesma coisa
Jordan Shurmer 25/10
5

StringEscapeUtils.escapeJavaScriptEu também StringEscapeUtils.escapeEcmaScriptdeveria fazer o truque.

Hanubindh Krishna
fonte
10
escapeJavaScriptescapa aspas simples como \', o que está incorreto.
27514 laurt
4

Se você estiver usando fastexml jackson, poderá usar o seguinte: com.fasterxml.jackson.core.io.JsonStringEncoder.getInstance().quoteAsString(input)

Se você estiver usando o codehaus jackson, poderá usar o seguinte: org.codehaus.jackson.io.JsonStringEncoder.getInstance().quoteAsString(input)

Dhiraj
fonte
3

Não sei ao certo o que você quer dizer com "criando json manualmente", mas você pode usar algo como gson ( http://code.google.com/p/google-gson/ ), que transformaria seu HashMap, Array, String etc. , para um valor JSON. Eu recomendo ir com uma estrutura para isso.

Vladimir
fonte
2
Por manual, eu quis dizer não usar uma biblioteca JSON como Simple JSON, Gson ou XStream.
Behrang Saeedzadeh
Apenas uma questão de curiosidade - por que você não gostaria de usar uma dessas APIs? É como tentar escapar URLs manualmente, em vez de usar URLEncode / Decode ...
Vladimir
1
Na verdade, não é a mesma coisa, essas bibliotecas vêm com muito mais do que o equivalente a URLEncode / Decode, incluem um pacote de serialização inteiro para permitir a persistência do objeto java no formato json e, às vezes, você realmente só precisa codificar um pequeno grupo de texto
jmd 12/12
2
fazer uma criação manual dos JSON faz sentido, se você quiser não incluem biblioteca apenas para serialização pequenas bits de dados
Aditya Kumar Pandey
2
Eu pediria que um membro da equipe fosse removido de qualquer projeto em que participasse, se eles ousassem criar o JSON manualmente, onde existisse uma biblioteca de alta qualidade para isso.
Michael Joyce
2

Não gastei tempo para garantir 100% de certeza, mas funcionou para minhas entradas o suficiente para ser aceito pelos validadores JSON online:

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

embora não pareça melhor do que org.codehaus.jettison.json.JSONObject.quote("your string")

Eu simplesmente já uso ferramentas de velocidade no meu projeto - meu edifício "manual JSON" estava dentro de um modelo de velocidade

Tjunkie
fonte
2

Para aqueles que vieram aqui procurando uma solução de linha de comando, como eu, o --data-urlencode do cURL funciona bem:

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

envia

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

, por exemplo. Dados JSON maiores podem ser colocados em um arquivo e você usaria a sintaxe @ para especificar um arquivo a ser coletado nos dados a serem escapados. Por exemplo, se

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

você usaria

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

E agora, este também é um tutorial sobre como consultar o Freebase na linha de comando :-)

vijucat
fonte
2

Use a classe EscapeUtils na API comum.

EscapeUtils.escapeJavaScript("Your JSON string");
theJ
fonte
1
Observe que aspas simples, por exemplo, são tratadas de maneira diferente ao se deslocar para javascript ou json. Em commons.lang 3.4 StringEscapeUtils ( commons.apache.org/proper/commons-lang/javadocs/api-3.4/org/… ) possui um método escapeJSON que é diferente do método escapeJavaScript no commons.lang 2: commons.apache. org / adequada / commons-lang / javadocs / api-2.6 / org /…
GlennV 23/03
1

Considere a classe JsonWriter de Moshi . Possui uma API maravilhosa e reduz ao mínimo a cópia, tudo pode ser transmitido de maneira agradável para um arquivo, 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();

Se você quer a corda na mão:

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

Se você precisar escapar do JSON dentro da string JSON, use org.json.JSONObject.quote ("sua string json que precisa ser escapada") parece funcionar bem

webjockey
fonte
0

usando a sintaxe \ uXXXX pode resolver esse problema, google UTF-16 com o nome do sinal, você pode descobrir XXXX, por exemplo: utf-16 aspas duplas

David
fonte
0

Os métodos aqui que mostram a implementação real estão todos com defeito.
Eu não tenho código Java, mas apenas para o registro, você pode facilmente converter esse código C #:

Cortesia do mono-projeto @ 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();
}

Isso pode ser compactado em

    // 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
fonte
Como o quote()método descrito em outras respostas está com defeito?
31580 Sandy
0

Acho que a melhor resposta em 2017 é usar as APIs javax.json. Use javax.json.JsonBuilderFactory para criar seus objetos json e, em seguida, grave os objetos usando javax.json.JsonWriterFactory. Muito boa combinação de construtor / escritor.

ferreiros
fonte