Ir para o conteúdo
jsonbeautifiers
Português

Achatar JSON

Transforma JSON aninhado em caminhos de chave de um nível só, e desfaz a operação.

Aninhado
Achatado

Nada do que você cola sai do seu navegador. A lista de permissões connect-src transforma isso em uma garantia do navegador, e não em uma promessa. Confira você mesmo

Achatar transforma um documento aninhado em um único nível de caminhos com pontos: {"a":{"b":1}} vira {"a.b":1}. Desachatar traz de volta.

É o que você faz antes de carregar JSON em qualquer coisa retangular: uma planilha, um data frame, um armazenamento de feature flags, um arquivo de ambiente, um formulário.

A ida e volta, e o único caso que quebra

Achatar e depois desachatar devolve o documento original, incluindo objetos e arrays vazios, que várias implementações descartam caladas.

Existe exatamente um caso em que não dá: uma chave que contém o próprio separador. Dado {"a.b": 1}, o caminho achatado "a.b" é indistinguível de um {"a":{"b":1}} aninhado. Esta ferramenta detecta isso e avisa, em vez de produzir algo que não volta. Quando acontecer, escolha outro separador.

Arrays: notação de índice ou de colchetes

A notação com pontos dá tags.0 e tags.1. A de colchetes dá tags[0] e tags[1]. As duas fazem a ida e volta aqui, e o parser de entrada aceita qualquer uma.

A notação com pontos é a que o json_normalize do pandas produz e a que a maioria dos pipelines de CSV espera. A de colchetes é mais fácil de ler quando uma chave poderia plausivelmente ser numérica, porque tags[0] e tags.0 são ambíguos de um jeito que tags["0"] não é.

Onde achatar perde informação

Uma chave de objeto numérica fica indistinguível de um índice de array depois de achatada. {"2024": {"total": 1}} vira "2024.total", e desachatar isso com a detecção de arrays ligada produz um array com 2024 posições vazias.

Desligue "chaves numéricas como arrays" quando as suas chaves forem de fato strings numéricas, o que é comum em qualquer coisa indexada por ano, por código de status HTTP ou por ID.

How to do this in code

Achatando em código.

py Python, pandas

record_path é o argumento que transforma uma relação de um para muitos em linhas em vez de colunas numeradas.

import pandas as pd

# The workhorse. sep defaults to '.'
df = pd.json_normalize(records)

# Explode a nested array into one row per element
df = pd.json_normalize(
    records,
    record_path='items',
    meta=['id', 'created_at'],
)
sh jq
# Every leaf as a dotted path
jq -r 'paths(scalars) as $p | "\($p | join(".")) = \(getpath($p))"' in.json

# A flat object rather than lines
jq '[leaf_paths as $p | {(($p | map(tostring) | join("."))): getpath($p)}] | add' in.json
js JavaScript

O ramo dos contêineres vazios é a linha que a maioria das implementações deixa de fora, e é por isso que elas não fazem a ida e volta.

function flatten(value, prefix = '', out = {}) {
  if (value && typeof value === 'object') {
    const entries = Array.isArray(value)
      ? value.map((v, i) => [i, v])
      : Object.entries(value);
    if (entries.length === 0) {
      out[prefix] = value;      // preserve {} and []
      return out;
    }
    for (const [k, v] of entries) {
      flatten(v, prefix ? `${prefix}.${k}` : String(k), out);
    }
    return out;
  }
  out[prefix] = value;
  return out;
}

Perguntas frequentes

Qual separador devo usar?
Um ponto, a não ser que as suas chaves contenham pontos. O sublinhado é a segunda escolha de costume, e a barra é útil quando o resultado vai para algo que já pensa em caminhos.
Dá para achatar só uma parte do documento?
Defina um limite de profundidade. Tudo além dele fica como valor aninhado, que é o que você quer quando a parte profunda é um bloco opaco que você guarda em vez de consultar.
O que acontece com null?
Ele é mantido por padrão, como uma chave achatada com valor null. Existe uma opção para omitir os nulls, útil para um diff e perigosa para a ida e volta.