Geonode logo
Geonode Team

Geonode Team

Atualizado: 7 de outubro de 2026

Publicado: 2 de setembro de 2026

Como ler um ficheiro JSON em JavaScript

«Ler um ficheiro JSON em JavaScript» significa três coisas diferentes, dependendo do local onde o código é executado, e os métodos não são compatíveis entre si. No Node, lê-se a partir do disco. Num navegador, obtém-se o ficheiro através da rede ou aceita-se um ficheiro escolhido pelo utilizador. E, em ambos os casos, existe agora uma sintaxe de importação padrão que a maioria das pessoas ainda não adotou. Este guia aborda todas estas situações, além do tratamento de erros que transforma uma falha confusa numa falha óbvia.

Uma breve nota sobre o motivo pelo qual uma empresa de proxy escreve isto: somos a Geonode, e o erro JSON mais comum que os nossos clientes relatam é «Unexpected token '<'» nos dados que obtiveram. Isso significa que a resposta era HTML — uma página de erro, um redirecionamento para início de sessão ou uma página de bloqueio — e o analisador está a indicar corretamente que não recebeu JSON. Antes de alterar qualquer código, registe os primeiros 200 caracteres do que recebeu. Quase tudo o resto neste artigo pressupõe que o ficheiro é, de facto, JSON, e é essa suposição que falha com mais frequência.

No Node: Ler a partir do disco

Três abordagens, sendo que uma delas é a solução mais atual.

**fs/promises

, a abordagem padrão:**

import { readFile } from "node:fs/promises";

const raw = await readFile("./data.json", "utf8");
const data = JSON.parse(raw);

A documentação do Node é específica quanto ao argumento de codificação, e isso é importante: sem uma codificação, readFile

«retorna uma promessa que se cumpre com um objeto <Buffer>

contendo o conteúdo do ficheiro»; com uma, «cumpre-se com um <string>

». JSON.parse

aceita um Buffer convertendo-o para uma string, pelo que omitir a codificação normalmente funciona e implica uma conversão adicional sem motivo. Passe "utf8"

.

Também suporta um AbortSignal

através da opção signal

, «permitindo-lhe abortar uma operação readFile

em curso» — útil quando uma leitura faz parte de um pedido que pode ser cancelado.

Síncrono, para código de arranque:

import { readFileSync } from "node:fs";
const config = JSON.parse(readFileSync("./config.json", "utf8"));

O bloqueio é aceitável antes de o servidor começar a servir. Não é aceitável dentro de um manipulador de pedidos, onde paralisa o ciclo de eventos para todas as outras ligações. Essa distinção é a regra fundamental.

**require

, apenas em CommonJS:**

const data = require("./data.json");

É conciso e apresenta duas propriedades que as pessoas tendem a esquecer. Armazena em cache, pelo que um segundo require

com o mesmo caminho devolve o mesmo objeto sem voltar a ler o ficheiro — o que significa que editar o ficheiro em tempo de execução não tem qualquer efeito. Além disso, não está disponível em módulos ES.

Importar atributos: a forma padrão

A sintaxe para a qual a maioria das pessoas ainda não mudou e que deve ser utilizada em código novo.

import data from "./data.json" with { type: "json" };

Ou dinamicamente:

const data = await import("./data.json", { with: { type: "json" } });

MDN regista isto como Baseline 2025, disponível desde abril de 2025 nos navegadores mais recentes, com ambientes de execução que não são navegadores, como o Node e o Deno, a alinharem-se com a semântica dos navegadores para módulos JSON.

O atributo type: "json"

não é meramente decorativo. O MDN explica que este «valida se um módulo é servido com o tipo MIME application/json

» e que, se o ficheiro «for servido com qualquer tipo de mídia que não seja application/json

, a importação falhará».

Vale a pena citar a justificação de segurança, pois explica por que razão o atributo é obrigatório e não opcional:

Se, por alguma razão (por exemplo, se o servidor for pirateado ou for falso), o tipo de mídia na resposta do servidor estiver definido como text/javascript

(para código-fonte JavaScript), então o ficheiro seria analisado e executado como código. Se o ficheiro «JSON» contiver, de facto, código malicioso, a declaração import

executaria involuntariamente código externo, representando uma séria ameaça.

Uma nota sobre a migração: uma proposta anterior utilizava a palavra-chave «assert

» em vez de «with

». O MDN assinala isto como uma alteração compatibilidade — as implementações que utilizam «assert

» «já não são suportadas». Se encontrar «assert { type: "json" }

» em código mais antigo ou num tutorial, é necessário atualizá-lo.

No navegador: Obter JSON

O caso mais comum, e aquele que esconde uma armadilha.

const res = await fetch("/data.json");
if (!res.ok) throw new Error(`HTTP ${res.status} from ${res.url}`);
const data = await res.json();

A verificação «res.ok» não é opcional, e ignorá-la é a causa direta do erro mencionado na introdução deste artigo. O fetch não rejeita os códigos de erro HTTP — um 403, um 404 e um 500 são todos resolvidos normalmente. Chamar .json() faz com que se tente analisar uma página de erro, e obtém-se um erro de sintaxe relativo a um carácter < que nada tem a ver com o seu JSON.

Para uma versão que falhe de forma útil:

async function fetchJson(url) {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status} from ${url}`);
  const type = res.headers.get("content-type") ?? "";
  if (!type.includes("application/json")) {
    const body = await res.text();
    throw new Error(`Expected JSON, got ${type}: ${body.slice(0, 200)}`);
  }
  return res.json();
}

Duas linhas de verificação convertem um erro de análise opaco numa mensagem que indica o estado, o tipo de conteúdo e o que realmente chegou.

Note também que Response.json() não aceita uma função «reviver». Se precisar de uma — para conversão de datas ou para lidar com números inteiros grandes — utilize res.text() seguido de JSON.parse. Abordámos as funções «reviver» e o problema da precisão no nosso guia sobre JSON.parse.

No navegador: um ficheiro escolhido pelo utilizador

Para um ficheiro selecionado a partir do computador do utilizador, utilize a API File.

<input type="file" id="picker" accept="application/json">
document.getElementById("picker").addEventListener("change", async e => {
  const file = e.target.files[0];
  if (!file) return;
  try {
    const data = JSON.parse(await file.text());
    console.log(data);
  } catch (err) {
    console.error(`Could not parse ${file.name}: ${err.message}`);
  }
});

File.text() devolve uma promessa que resolve para o conteúdo como uma cadeia de caracteres, o que é consideravelmente mais simples do que a antiga FileReader com os seus manipuladores de eventos. FileReader continua a ser o que precisa se pretender eventos de progresso num ficheiro muito grande.

Há duas coisas a ter em conta. Os navegadores não conseguem ler caminhos locais arbitrários — o utilizador tem de escolher o ficheiro, e isso é uma restrição de segurança deliberada, em vez de uma limitação a contornar. Além disso, uma extensão do tipo .json não garante nada quanto ao conteúdo, pelo que o try /catch está a realizar o trabalho efetivo.

A função «arrastar e largar» utiliza os mesmos objetos File , obtidos a partir de event.dataTransfer.files .

Tratamento de erros que te dá informação

O hábito que faz a diferença entre um problema de cinco minutos e um de uma hora.

function parseJson(text, source) {
  try {
    return JSON.parse(text);
  } catch (err) {
    throw new Error(
      `Failed to parse JSON from ${source}: ${err.message}. ` +
      `First 200 chars: ${text.slice(0, 200)}`
    );
  }
}

O que importa é a fatia. JSON.parse Os erros indicam uma posição e um caractere; a entrada real explica o motivo. Três assinaturas cobrem a maioria dos casos:

Unexpected token '<' — o conteúdo é HTML. Uma página de erro, um redirecionamento de início de sessão ou uma lista de diretórios.

Unexpected end of JSON input — a entrada está vazia ou truncada. Uma resposta 204, um ficheiro que não foi gravado na totalidade ou uma recuperação que se esqueceu de aguardar.

Unexpected token '}' numa posição plausível — JSON genuinamente malformado, frequentemente com uma vírgula final. O JSON proíbe-as, embora o JavaScript as permita.

Para leituras de ficheiros, distinga a falha de leitura da falha de análise. ENOENT significa que o ficheiro não existe, o que é um problema diferente de conteúdos inválidos e merece uma mensagem diferente.

Validar o que leu

A análise sintática foi bem-sucedida. Isso indica apenas que a sintaxe era válida e nada sobre se os dados têm o formato que o seu código espera — e é precisamente nessa diferença entre os dois que reside uma percentagem surpreendente de falhas em produção.

A análise sintática e a validação são etapas distintas. JSON.parse

irá devolver sem problemas {"user": {"nmae": "Ada"}}

com um erro ortográfico na chave, ou um campo price

contendo a cadeia "n/a"

quando se esperava um número. O seu código falha então algures a jusante, várias funções afastadas do problema real, com um erro que identifica um sintoma em vez de uma causa.

Para tudo o que estiver fora do seu controlo, valide em relação a um esquema. Várias bibliotecas lidam bem com isto, e o padrão é o mesmo independentemente da que escolher:

const Config = z.object({
  port: z.number().int().min(1).max(65535),
  host: z.string(),
  retries: z.number().int().default(3),
  features: z.array(z.string()).optional(),
});

const config = Config.parse(JSON.parse(await readFile("./config.json", "utf8")));

A falha indica agora o campo, o tipo esperado e o que foi encontrado — o que faz a diferença entre uma correção de cinco minutos e uma tarde inteira.

Para casos simples, algumas asserções não custam nada:

const data = JSON.parse(raw);
if (!Array.isArray(data.items)) throw new Error("items must be an array");
if (data.items.length === 0) throw new Error("items is empty — check the source");

Essa segunda verificação vale mais do que parece. Um array vazio é um JSON válido, é analisado corretamente e, muitas vezes, é um sintoma em vez de um resultado legítimo — uma API que não devolveu nada porque um filtro estava errado, ou uma extração que teve sucesso numa página que tinha sido alterada.

Desconfie de números que não tenha gerado. Os números JSON transformam-se em valores «double» do JavaScript, pelo que os inteiros superiores a Number.MAX_SAFE_INTEGER

perdem precisão silenciosamente, sem que seja apresentado qualquer erro. Os identificadores são as vítimas habituais: dois registos distintos numa base de dados podem ser analisados como tendo o mesmo valor. Se um campo for um identificador em vez de uma quantidade, deve ser uma cadeia de caracteres no JSON — e, se não controlares quem o produziu, o argumento ``context.source`

` do reviver fornece-te os dígitos originais.

E valide no ponto de entrada, uma única vez. Verificar a estrutura dos dados no momento em que estes entram no seu programa significa que tudo a jusante pode partir do princípio de que estão corretos. Verificar de forma defensiva em vinte locais significa vinte locais para atualizar e nenhum ponto único onde o contrato esteja definido por escrito.

Ficheiros grandes: a função `

JSON.parse

` é síncrona e necessita de ter o documento inteiro na memória. Ambas as situações tornam-se problemáticas à medida que os ficheiros aumentam de tamanho.

Orientação geral. Se for inferior a um megabyte, nem vale a pena pensar nisso. Entre um e dez, avalie a situação — especialmente na thread principal de um navegador, onde a análise bloqueia a renderização e provoca lentidão visível. Acima de dez megabytes, ou acima de cerca de um décimo da sua memória disponível, opte por outra solução.

Retire-o da thread principal. Num navegador, um Web Worker analisa sem congelar a interface. No Node, uma thread de trabalho faz o mesmo para o ciclo de eventos.

Utilize JSON delimitado por novas linhas. Trata-se de uma correção estrutural, em vez de uma solução alternativa. Um documento JSON por linha significa que processa um ficheiro de qualquer tamanho com memória constante, cada linha é analisada de forma independente e um ficheiro truncado continua a fornecer todos os registos completos:

import { createReadStream } from "node:fs";
import { createInterface } from "node:readline";

const rl = createInterface({ input: createReadStream("./data.jsonl") });
for await (const line of rl) {
  if (line.trim()) handle(JSON.parse(line));
}

Se controlar o formato, este é o melhor design para qualquer coisa que seja acrescentada ao longo do tempo — e a razão pela qual uma tarefa de recolha que falhe deixa um ficheiro utilizável em vez de um que não possa ser analisado.

Utilize um analisador de streaming quando o formato for um único array grande que não possa alterar. Várias bibliotecas emitem valores à medida que estes chegam, em vez de construírem a árvore na íntegra.

Ou peça menos. Paginação, seleção de campos, um ponto de extremidade mais restrito. Quase sempre a resposta correta e quase sempre ignorada, porque requer falar com quem é responsável pela API.

Gravar JSON de volta

A situação inversa, resumidamente, já que costuma ser a próxima pergunta.

import { writeFile } from "node:fs/promises";
await writeFile("./out.json", JSON.stringify(data, null, 2), "utf8");

Os argumentos ``null, 2`

` produzem uma saída indentada, o que é mais importante do que parece: um ficheiro que venha a ser lido por uma pessoa ou comparado num sistema de controlo de versões deve estar formatado, enquanto um ficheiro que seja transmitido não deve estar.

Três coisas que não sobrevivem a ``JSON.stringify`

e provocam perda silenciosa de dados em vez de erros. Os valores ``undefined

e as funções são totalmente removidos dos objetos e transformam-se em ``null

dentro de matrizes. Os objetos ``Date

transformam-se em cadeias de caracteres ISO, pelo que não voltam a ser convertidos em datas sem um «reviver». E ``BigInt

` provoca um erro imediato — a abordagem padrão consiste em serializar inteiros grandes como cadeias de caracteres.

Para acrescentar dados, escreva JSON delimitado por linhas novas em vez de reescrever uma matriz:

import { appendFile } from "node:fs/promises";
await appendFile("./log.jsonl", JSON.stringify(record) + "\n", "utf8");

Acrescentar dados a uma matriz JSON requer lê-la, analisá-la, adicionar elementos e reescrever o ficheiro inteiro — o que é dispendioso e corrompe o ficheiro se o processo falhar a meio da gravação.

Perguntas frequentes

Como posso ler um ficheiro JSON no Node.js?

const data = JSON.parse(await readFile("./data.json", "utf8")) utilizando node:fs/promises. Ou utilize a sintaxe de importação padrão: import data from "./data.json" with { type: "json" }, que é a norma de referência a partir de 2025 e funciona tanto no Node como nos navegadores.

O JavaScript consegue ler um ficheiro local no navegador?

Não através do caminho. Os navegadores não conseguem abrir ficheiros locais arbitrários, o que constitui uma restrição de segurança deliberada. O utilizador deve selecionar um ficheiro através de um «<input type="file">» ou de arrastar e largar; em seguida, File.text() apresenta-lhe o conteúdo.

O que faz import ... with { type: "json" }?

Importa um ficheiro JSON como módulo, ao mesmo tempo que valida se o servidor o serviu com o tipo MIME application/json. Sem essa verificação, um ficheiro servido como text/javascript seria analisado e executado como código — o que constitui o problema de segurança que este atributo existe para impedir.

Por que recebo a mensagem «Token inesperado '<'» ao ler JSON?

Porque o conteúdo começa com <, o que significa que recebeu HTML em vez de JSON — normalmente uma página de erro ou um redirecionamento para início de sessão. Com fetch, a causa é quase sempre a ausência de uma verificação res.ok, uma vez que fetch não rejeita em caso de estados de erro HTTP.

Devo usar require ou import para JSON?

Use import ... with { type: "json" } em código novo, uma vez que é o padrão e funciona em módulos ES. require é exclusivo do CommonJS e armazena o resultado em cache, pelo que um ficheiro editado em tempo de execução não será relido. Nenhuma das duas opções é adequada para ficheiros que se alteram enquanto o programa está a ser executado — use readFile para esses casos.

Como posso ler um ficheiro JSON muito grande?

Transfira a análise para fora da thread principal utilizando um worker, ou reestruture os dados como JSON delimitado por novas linhas, para que cada linha seja analisada de forma independente numa memória constante. Para um grande array imutável, utilize um analisador de streaming. E considere se é possível solicitar menos dados desde o início.

Qual é a diferença entre o JSON.parse e o response.json()?

O Response.json() lê o corpo de uma resposta e analisa-o numa única etapa, não aceitando uma função de recuperação. O JSON.parse trabalha com uma string que já possui e aceita uma função de recuperação. Se precisar de uma função de recuperação — para datas ou inteiros grandes — utilize res.text() seguido de JSON.parse.

Como devo lidar com um ficheiro JSON que pode não existir?

Deteta o erro de leitura separadamente do erro de análise. No Node, um código de erro ENOENT significa que o ficheiro não existe, o que normalmente requer um valor por defeito em vez de uma falha — enquanto que um SyntaxError significa que o ficheiro existe, mas o seu conteúdo está incorreto.

Conclusão

O método depende do ambiente, e a abordagem atual é mais uniforme do que costumava ser. O import data from "./data.json" with { type: "json" } funciona no Node e nos navegadores, é considerado «Baseline» a partir de 2025 e inclui uma verificação do tipo MIME que existe por uma razão de segurança real, e não apenas por uma questão de formalidade.

Para ficheiros que sofrem alterações enquanto o programa está a ser executado, leia-os explicitamente — readFile com uma codificação "utf8" no Node, fetch com uma verificação res.ok num navegador e File.text() para algo escolhido pelo utilizador. Essa verificação res.ok é a linha de maior valor deste artigo, porque ignorá-la é a causa direta do erro JSON mais comum que existe.

Quando algo falhar, registe os primeiros 200 caracteres da entrada antes de alterar qualquer código. «Unexpected token '<'» significa HTML, «Unexpected end of JSON input» significa vazio ou truncado, e ambas as respostas são obtidas analisando o que realmente chegou, em vez de se basear em suposições sobre o analisador.

E se os ficheiros estiverem a ficar grandes, a solução estrutural é utilizar JSON delimitado por novas linhas, em vez de recorrer a um equipamento mais potente. Um documento por linha é processado em memória constante, permite anexar dados com segurança e sobrevive a uma gravação interrompida com todos os registos completos intactos.