Como escapar strings JSON: caracteres, stringify e armadilhas
Escapar uma string JSON significa transformar um texto qualquer em uma string que pode ficar com segurança dentro de um documento JSON como literal de string. Um punhado de caracteres — as aspas duplas, a contrabarra e caracteres de controle como quebra de linha e tabulação — carregam significado estrutural ou simplesmente são ilegais dentro de uma string JSON, então cada um deles é substituído por uma sequência de escape segura como \", \\ ou \n. Faça isso errado e seu payload deixa de ser analisado.
Você cai nisso o tempo todo: ao aninhar um objeto JSON dentro de outro como campo de string, ao colar um trecho de código de várias linhas em um valor de configuração ou ao montar à mão o corpo de uma requisição REST para o curl. Este guia mostra exatamente quais caracteres precisam de escape, esclarece a confusão entre escapar e JSON.stringify, percorre o aninhamento JSON-em-JSON e os escapes Unicode, e lista as armadilhas que quebram payloads de forma silenciosa. Se você só quer escapar algo agora mesmo, nossa ferramenta Escape JSON faz isso no navegador — mas continue lendo para entender por que ela funciona do jeito que funciona.
O que é o escape de strings JSON?
O escape de strings JSON é o processo de converter uma string bruta em uma forma segura para embutir dentro de um documento JSON. O JSON reserva um pequeno conjunto de caracteres que carregam significado estrutural: as aspas duplas " delimitam uma string, e a contrabarra \ inicia uma sequência de escape. Os caracteres de controle abaixo de U+0020, como quebras de linha, tabulações e retornos de carro, também não podem aparecer literalmente dentro de uma string JSON. O escape substitui cada um deles por uma sequência segura para que a string resultante seja analisada corretamente.
Quando você realmente precisa disso? Algumas situações aparecem o tempo todo:
- JSON-em-JSON: um envelope de webhook, uma mensagem Kafka ou um log de auditoria guarda o corpo de uma requisição como campo de string, então o JSON interno precisa ser escapado antes de poder ser atribuído.
- Configuração escrita à mão: jogar um script de shell, uma consulta SQL ou um trecho de código de várias linhas em um único valor JSON significa transformar cada quebra de linha em
\n. - Corpos de requisição REST: montar à mão um corpo JSON para o
curlou um cliente HTTP, onde aspas e quebras de linha precisam sobreviver ao shell e à rede. - Codificação segura para logs: gravar conteúdo fornecido pelo usuário em uma linha de log estruturada sem deixar que uma aspa ou quebra de linha injetada corrompa o formato.
Uma palavra rápida sobre a ordem das operações. Se você está partindo de um JSON bagunçado ou não confiável, valide-o primeiro para escapar algo bem formado — cole-o no Formatador JSON para formatar e conferir, depois escape o resultado limpo. Escapar lixo só dá lixo escapado.
Quais caracteres precisam de escape em JSON
A especificação JSON define uma lista precisa e curta. Sete caracteres têm um escape dedicado de dois caracteres, e todo o resto abaixo de U+0020 recai sobre um escape Unicode \uXXXX. Aqui está o conjunto completo de caracteres de escape JSON:
| Caractere | Escapa para | Observações |
|---|---|---|
" (U+0022) | \" | Delimitador de string |
\ (U+005C) | \\ | Início do escape (o caso da contrabarra de escape JSON) |
| quebra de linha (U+000A) | \n | |
| retorno de carro (U+000D) | \r | |
| tabulação (U+0009) | \t | |
| backspace (U+0008) | \b | |
| avanço de formulário (U+000C) | \f | |
| outros controles < U+0020 | \uXXXX | ex.: U+0000 → \u0000 |
O que não precisa de escape é igualmente importante. A barra / é um caractere perfeitamente normal (escapá-la é opcional, e só é útil em um caso bem específico abordado adiante). Aspas simples nunca precisam de escape porque o JSON não as usa como delimitadores. E todo caractere imprimível em U+0020 ou acima — incluindo todos os caracteres UTF-8 de múltiplos bytes como é, 日 ou 😀 — é válido como está.
Aqui está a diferença na prática. A entrada bruta à esquerda, o literal de string JSON escapado à direita:
Input:
She said "hello" then left.
Escaped:
"She said \"hello\"\tthen left."
As aspas duplas viraram \" e a tabulação virou \t. Agora a string está segura para ser colocada em qualquer parser JSON, linha de log ou corpo de requisição.
JSON Escape vs JSON Stringify: qual é a diferença?
Este é o ponto que a maioria dos tutoriais pula, e confunde muita gente. Escapar e JSON.stringify não são duas operações diferentes — são duas visões da mesma coisa.
JSON.stringify(value) serializa qualquer valor JavaScript em sua representação textual JSON. Quando esse valor é uma string, serializá-la significa envolvê-la em aspas duplas e escapar os caracteres especiais lá dentro. Isso é exatamente o escape JSON. Então JSON.stringify("a\tb") retorna a string de sete caracteres "a\tb", aspas incluídas.
A questão prática é se você quer essas aspas externas ou não. Isso mapeia diretamente para a opção Envolver em aspas duplas da ferramenta Escape JSON:
| Modo | Saída para a entrada a"b | Quando usar |
|---|---|---|
| Envolver ativado | "a\"b" | Um literal de string JSON completo, idêntico ao JSON.stringify. Atribua a uma variável ou cole depois de dois-pontos. |
| Envolver desativado | a\"b | Apenas o corpo escapado, sem as aspas ao redor. Use quando você mesmo está digitando as aspas em um documento JSON. |
Então, se você pesquisou por “json stringify” e chegou aqui, o modelo mental é simples: aplicar stringify a uma string = escape com envolver ativado. A forma sem aspas é a mesma coisa com as aspas externas removidas.
Como escapar uma string para JSON no código
A regra de ouro: nunca monte à mão uma corrente de chamadas replace(). Toda linguagem de uso geral traz um serializador JSON que lida com aspas, contrabarras, caracteres de controle e Unicode corretamente. Recorra a ele.
JavaScript
const text = 'She said "hi"\nthen left.';
const escaped = JSON.stringify(text);
console.log(escaped);
// "She said \"hi\"\nthen left."
JSON.stringify em uma string dá o literal completo e com aspas. Quer só o corpo? Corte o primeiro e o último caractere: JSON.stringify(text).slice(1, -1).
Python
import json
text = 'She said "hi"\nthen left.'
print(json.dumps(text))
# "She said \"hi\"\nthen left."
print(json.dumps(text, ensure_ascii=False))
# "She said \"hi\"\nthen left." (não-ASCII mantido como UTF-8)
json.dumps usa por padrão ensure_ascii=True, que escapa todo caractere não-ASCII para \uXXXX — o mesmo comportamento do modo ASCII-seguro da ferramenta. Passe ensure_ascii=False para manter o UTF-8 bruto.
PHP
<?php
$text = "café \"quoted\"\nline";
echo json_encode($text);
// "caf\u00e9 \"quoted\"\nline" (por padrão escapa não-ASCII para \uXXXX)
echo json_encode($text, JSON_UNESCAPED_UNICODE);
// "café \"quoted\"\nline"
json_encode escapa tanto caracteres não-ASCII quanto barras por padrão. Adicione JSON_UNESCAPED_UNICODE para manter os acentos legíveis, e JSON_UNESCAPED_SLASHES para deixar o / em paz.
Go e Java
Em Go, json.Marshal(text) retorna os bytes escapados e com aspas:
b, _ := json.Marshal(`a "quoted" line`)
// b == `"a \"quoted\" line"`
Em Java, objectMapper.writeValueAsString(text) do Jackson ou JSONObject.quote(text) do org.json produzem o mesmo literal com aspas. Seja qual for a linguagem, apoie-se na biblioteca — ela já conhece cada caso extremo que você esqueceria.
Embutindo JSON dentro de JSON (JSON-em-JSON)
Este é o motivo mais comum para as pessoas escaparem JSON à mão. Um envelope de webhook, um registro de fila de mensagens ou um log de auditoria muitas vezes guarda um corpo de requisição inteiro como campo de string. Para fazer isso, o JSON interno precisa ser escapado antes.
Veja um pequeno objeto passar por duas camadas de codificação:
1. Inner object: {"a":1}
2. Escaped as a string: "{\"a\":1}"
3. Placed in envelope: {"payload": "{\"a\":1}"}
Cada " do objeto interno virou \", e o conjunto todo foi envolvido em um par externo de aspas. O resultado é um único valor de string válido que você pode atribuir a payload.
O detalhe do aninhamento mais profundo é que as contrabarras se multiplicam. Escapar uma string já escapada também escapa suas contrabarras, então cada camada quase dobra o número delas: uma aspa interna que era \" vira \\\" um nível acima, e \\\\\" mais um nível acima. JSON-em-JSON com três níveis de profundidade fica difícil de ler de cabeça, e aí uma ferramenta poupa o trabalho. Para ir na direção oposta e tirar o objeto interno de volta de dentro da string, passe-a pela nossa ferramenta Des-escape de JSON.
Unicode e escape \uXXXX
Por padrão, o JSON fica feliz com UTF-8 bruto. Um é continua um é, um 日 continua um 日, e o documento fica mais legível por isso. Você não precisa escapar nenhum caractere Unicode imprimível.
Então, quando você recorreria à saída ASCII-segura \uXXXX? Apenas quando um sistema a jusante não pode receber UTF-8 com confiança: gateways SOAP ou XML antigos, certos pipelines de logging, cabeçalhos de e-mail ou arquivos-fonte que precisam permanecer puramente ASCII. No modo ASCII-seguro, todo caractere acima de U+007F vira um escape \uXXXX — café se transforma em caf\u00e9. Fica mais barulhento, mas é ASCII byte a byte, e decodifica de volta para o original em qualquer parser compatível.
Há uma sutileza. \uXXXX codifica uma única unidade de código UTF-16 de 16 bits, mas caracteres fora do Plano Multilíngue Básico — emoji, escritas raras — precisam de 21 bits. O JSON lida com eles por meio de um par substituto (surrogate pair): dois escapes \uXXXX lado a lado. Uma carinha sorridente 😀 (U+1F600) vira \ud83d\ude00. A maioria dos serializadores faz isso por você; o perigo é um escapador escrito à mão que emite um substituto solitário e sem par.
Se pares substitutos e pontos de código forem território novo, o Guia UTF-8 vs UTF-16 vs Unicode detalha exatamente como um único caractere mapeia para bytes e unidades de código. É o contexto que falta por trás do motivo de um emoji precisar de dois escapes.
Des-escape: lendo de volta o JSON escapado
O escape tem um inverso. Para transformar "a\tb" de volta no texto real com duas linhas ou tabulado, você o analisa: JSON.parse(str) em JavaScript, json.loads(str) em Python. O parser percorre cada sequência de escape e reconstrói os caracteres originais, pares substitutos incluídos.
Quando o des-escape falha, o erro é quase sempre “invalid escape sequence”, e ele tem algumas causas comuns:
- Uma contrabarra solitária antes de um caractere que o JSON não reconhece como escape, como
\q. - Um escape inventado como
\x41— o JSON não tem o escape hexadecimal\x; ele só usa\u. - Um escape
\utruncado com menos de quatro dígitos hexadecimais, como\u00. - Uma aspa dupla solta ou desbalanceada que quebra o limite da string.
Verifique se toda contrabarra inicia um dos escapes válidos (\n \r \t \b \f \" \\ \/ \uXXXX) e se as aspas estão pareadas. Para strings escapadas copiadas do meio de uma linha de log — onde as aspas externas ficaram para trás — nossa ferramenta Des-escape de JSON aceita o corpo com ou sem aspas ao redor e o decodifica de qualquer forma.
Armadilhas comuns ao escapar JSON
A maioria dos payloads quebrados remonta a um destes seis erros.
1. Escape duplo. Escapar um texto que já estava escapado transforma \n em \\n e \" em \\\", então o consumidor lê uma contrabarra-n literal em vez de uma quebra de linha. Isso costuma acontecer quando um serviço a montante já escapou o valor em JSON e você escapa de novo. Faça o des-escape primeiro para checar o estado atual, depois escape exatamente uma vez.
2. Esquecer as aspas externas. Com envolver desativado você obtém só o corpo escapado, não uma string completa. Colar hello \"world\" diretamente onde se espera um valor JSON é inválido porque faltam as aspas ao redor. Ou mantenha o envolver ativado ou digite as aspas você mesmo.
3. Escape excessivo de não-ASCII. Ligar o modo ASCII-seguro quando o consumidor lida bem com UTF-8 só incha a saída. café vira caf\u00e9 sem motivo: fica mais difícil de ler e mais pesado na rede, sem nenhum ganho. Deixe desligado a menos que um sistema legado específico exija ASCII puro.
4. Escapar a barra por reflexo. O escape do / importa em exatamente um lugar: JSON embutido dentro de uma tag HTML <script>, onde a substring </script> fecharia a tag cedo demais independentemente do contexto JSON. Escapar / para \/ neutraliza isso. Fora desse único caso, escapar barras só adiciona ruído, então deixe desligado para corpos REST, arquivos de configuração e payloads de mensagens.
5. Correntes de replace feitas à mão. Um pipeline manual replace('"', '\\"') quase sempre esquece de algo — um caractere de controle, um backspace, um par substituto. Use o serializador da linguagem, que cobre a especificação inteira.
6. Escapar mas nunca des-escapar (ou des-escapar duas vezes). Uma ida e volta precisa estar equilibrada. Escape uma vez na entrada, des-escape uma vez na saída. Des-escape duas vezes e você estraga contrabarras reais que faziam parte dos dados.
Mais uma distinção que vale fixar: o escape JSON não é codificação de URL nem codificação por porcentagem. Eles resolvem problemas diferentes para transportes diferentes, e misturá-los (codificar um valor por porcentagem e depois escapá-lo em JSON, ou vice-versa) produz uma bagunça que nenhum dos parsers consegue ler direito. O Guia de codificação e decodificação de URL cobre quando a codificação por porcentagem é a ferramenta certa e como ela difere do que o JSON faz.
Perguntas frequentes
O que significa escapar uma string em JSON?
Significa substituir os caracteres que carregam significado estrutural para o JSON — as aspas duplas, a contrabarra e caracteres de controle como quebra de linha e tabulação — por sequências de escape seguras como \", \\ e \n. O resultado pode ser embutido como literal de string dentro de um documento JSON sem quebrar a análise.
Quais caracteres precisam de escape em JSON?
As aspas duplas, a contrabarra, a quebra de linha, o retorno de carro, a tabulação, o backspace e o avanço de formulário cada um recebe um escape dedicado, e todo outro caractere de controle abaixo de U+0020 vira \uXXXX. Caracteres imprimíveis e UTF-8 de múltiplos bytes não precisam de escape; a barra é opcional e só importa dentro de tags HTML <script>.
O escape JSON é o mesmo que JSON.stringify?
São duas visões da mesma operação. JSON.stringify aplicado a uma string a envolve em aspas duplas e escapa os caracteres especiais lá dentro, e isso é o escape JSON. Envolver ativado equivale à forma com aspas (idêntica ao JSON.stringify); envolver desativado dá só o corpo escapado sem as aspas ao redor.
Como escapo uma string para JSON em JavaScript ou Python?
Em JavaScript use JSON.stringify(str); em Python use json.dumps(str). Sempre confie na função embutida em vez de uma corrente de replace escrita à mão — as embutidas lidam corretamente com Unicode, caracteres de controle e todo caso extremo que você senão deixaria passar.
Por que meu JSON quebra com contrabarras a mais?
A causa habitual é o escape duplo: escapar um texto que já estava escapado, então \n vira \\n e o consumidor lê uma contrabarra-n literal em vez de uma quebra de linha. Faça o des-escape do valor primeiro para checar seu estado real, depois escape-o exatamente uma vez.
Preciso escapar barras ou Unicode em JSON?
Nenhum dos dois é obrigatório. A / é um caractere normal e só precisa de escape quando você embute JSON dentro de uma tag HTML <script>, para impedir que a sequência </script> a feche cedo demais. O Unicode permanece como UTF-8 bruto por padrão; use \uXXXX apenas quando um sistema a jusante não consegue lidar com UTF-8.