Formatação de dados JavaScript / impressora bonita

124

Estou tentando encontrar um caminho para pretty printuma estrutura de dados JavaScript em um formulário legível por humanos para depuração.

Eu tenho uma estrutura de dados bastante grande e complicada sendo armazenada em JS e preciso escrever um código para manipulá-lo. Para descobrir o que estou fazendo e onde estou errado, o que realmente preciso é poder ver a estrutura de dados na íntegra e atualizá-la sempre que fizer alterações na interface do usuário.

Todas essas coisas que eu consigo resolver, além de encontrar uma boa maneira de despejar uma estrutura de dados JavaScript em uma sequência legível por humanos. JSON faria, mas realmente precisa ser bem formatado e recuado. Normalmente, eu usaria o excelente material de dumping DOM do Firebug para isso, mas realmente preciso ver toda a estrutura de uma só vez, o que não parece ser possível no Firebug.

Todas as sugestões são bem-vindas.

Desde já, obrigado.

Dan
fonte
Não tenho certeza se você é notificado de edições de respostas. Então, escrevo este comentário para informar que adicionei minha própria versão do dump recuado. :-)
PhiLho
Nota: A resposta JSON.stringify () parece ser bastante útil, embora não seja aceita como 'a' resposta.
GuruM
Você pode obter uma saída visual e intuitiva de objetos usando nodedump: github.com/ragamufin/nodedump
ragamufin
Dê uma olhada lá: stackoverflow.com/questions/4810841/… #
monkeythedev

Respostas:

31

Eu escrevi uma função para despejar um objeto JS de forma legível, embora a saída não seja recuada, mas não deve ser muito difícil acrescentar isso: Eu criei essa função de uma que criei para Lua (que é muito mais complexa ) que tratou desse problema de recuo.

Aqui está a versão "simples":

function DumpObject(obj)
{
  var od = new Object;
  var result = "";
  var len = 0;

  for (var property in obj)
  {
    var value = obj[property];
    if (typeof value == 'string')
      value = "'" + value + "'";
    else if (typeof value == 'object')
    {
      if (value instanceof Array)
      {
        value = "[ " + value + " ]";
      }
      else
      {
        var ood = DumpObject(value);
        value = "{ " + ood.dump + " }";
      }
    }
    result += "'" + property + "' : " + value + ", ";
    len++;
  }
  od.dump = result.replace(/, $/, "");
  od.len = len;

  return od;
}

Vou tentar melhorar um pouco.
Nota 1: Para usá-lo, faça od = DumpObject(something)e use od.dump. Envolvido porque eu também queria o valor len (número de itens) para outra finalidade. É trivial fazer com que a função retorne apenas a string.
Nota 2: não trata de loops em referências.

EDITAR

Eu fiz a versão recuada.

function DumpObjectIndented(obj, indent)
{
  var result = "";
  if (indent == null) indent = "";

  for (var property in obj)
  {
    var value = obj[property];
    if (typeof value == 'string')
      value = "'" + value + "'";
    else if (typeof value == 'object')
    {
      if (value instanceof Array)
      {
        // Just let JS convert the Array to a string!
        value = "[ " + value + " ]";
      }
      else
      {
        // Recursive dump
        // (replace "  " by "\t" or something else if you prefer)
        var od = DumpObjectIndented(value, indent + "  ");
        // If you like { on the same line as the key
        //value = "{\n" + od + "\n" + indent + "}";
        // If you prefer { and } to be aligned
        value = "\n" + indent + "{\n" + od + "\n" + indent + "}";
      }
    }
    result += indent + "'" + property + "' : " + value + ",\n";
  }
  return result.replace(/,\n$/, "");
}

Escolha seu recuo na linha com a chamada recursiva e você poderá escolher o estilo alternando a linha comentada após esta.

... Vejo que você criou sua própria versão, o que é bom. Os visitantes terão uma escolha.

PhiLho
fonte
1
I como;) Não é possível fazê-lo funcionar corretamente, mas se você não se importa, eu vou descaradamente roubar o conceito e escrever meu próprio :)
Dan
2
Uma breve descrição dessa abordagem (comparada com o método JSON.stringify que Jason sugere) é que ela não mostra matrizes de objetos corretamente. Quando você tem uma matriz de objetos, ela aparece como [objeto Objeto].
22910 Ryan
@ Ryan: Você quer dizer objetos nativos do navegador? Sim, olhando para o meu código, vi que adicionei um comentário: // Pena que um campo seja um objeto ... :-P OK para o meu teste aqui ... Não há problema em despejar estruturas criadas pelo usuário. Vejo que existem alternativas abaixo, se você precisar de algo mais robusto.
PhiLho
Eu não posso usar isso. Recebo loop infinito quando tento despejar alguns dados json.
neoneye
1
@RaphaelDDL & PhiLho - O tamanho máximo da pilha de chamadas também pode ser acionado em um objeto pequeno; um com uma referência de propriedade para si mesmo. Essa referência causaria um loop infinito com esta função.
skibulk
233

Use o JSON.stringify de Crockford assim:

var myArray = ['e', {pluribus: 'unum'}];
var text = JSON.stringify(myArray, null, '\t'); //you can specify a number instead of '\t' and that many spaces will be used for indentation...

A variável textficaria assim:

[
  "e",
   {
      "pluribus": "unum"
   }
]

A propósito, isso não requer nada além do arquivo JS - ele funcionará com qualquer biblioteca etc.

Jason Bunting
fonte
5
Esta é quase definitivamente a melhor resposta que você receberá. Ensinei a 4 ou 5 não programadores a ler e editar estruturas de dados JSON.stringified e usá-las extensivamente para arquivos de configuração.
Joel Anair 24/09/08
1
Estranho que isso causasse problemas - ele introduz o nome "JSON" no espaço para nome global, o que pode causar problemas. Verifique seu espaço de nome para "JSON" antes de adicioná-lo para ver se existe uma colisão.
Jason Bunting
1
Bem, protótipo é mal assim ...;)
Jason Bunting
7
Uma atualização sobre isso, com o Firefox 3.5 e superior, JSON.stringify está embutida. ( developer.mozilla.org/En/Using_JSON_in_Firefox ), portanto, se você estiver apenas tentando ver um objeto JSON para fins de depuração, poderá fazê-lo sem dependências JS adicionais.
Greg Bernhardt
3
Também no Chrome. No entanto, o JSON.stringify falha em dados circulares JSON.stringify((function(){var x = []; x.push(x); return x})())e em muitos outros tipos de objetos JSON.stringify(/foo/).
Kragen Javier Sitaker
21

Você pode usar o seguinte

<pre id="dump"></pre>
<script>
   var dump = JSON.stringify(sampleJsonObject, null, 4); 
   $('#dump').html(dump)
</script>
Dharmanshu Kamra
fonte
15

Em Firebug, se você apenas console.debug ("%o", my_object)clicar nele no console e entrar em um explorador de objetos interativo. Ele mostra o objeto inteiro e permite expandir objetos aninhados.

John Millikin
fonte
1
O problema é que ele mostra apenas o objeto 'mais alto' - eu tenho dezenas de objetos aninhados e preciso realmente ver todo o conteúdo de uma só vez e, principalmente, ver onde as coisas estão mudando. Portanto, o Firebug realmente não está funcionando para mim neste caso.
Dan
(sim, eu sei que você pode clicar para expandi-los, mas clicando 10 ou assim liga toda vez que eu quiser despejar os dados é o que eu estou fazendo agora - progresso muito lento)
Dan
1
Isso também funciona no Chrome (e, portanto, presumivelmente no Safari).
Kragen Javier Sitaker
9

Para quem procura uma maneira incrível de ver seu objeto, verifique prettyPrint.js

Cria uma tabela com opções de exibição configuráveis ​​para serem impressas em algum lugar no seu documento. Melhor olhar do que no console.

var tbl = prettyPrint( myObject, { /* options such as maxDepth, etc. */ });
document.body.appendChild(tbl);

insira a descrição da imagem aqui

RaphaelDDL
fonte
6

Estou programando Rhinoe não estava satisfeito com nenhuma das respostas postadas aqui. Então, eu escrevi minha própria impressora bonita:

function pp(object, depth, embedded) { 
  typeof(depth) == "number" || (depth = 0)
  typeof(embedded) == "boolean" || (embedded = false)
  var newline = false
  var spacer = function(depth) { var spaces = ""; for (var i=0;i<depth;i++) { spaces += "  "}; return spaces }
  var pretty = ""
  if (      typeof(object) == "undefined" ) { pretty += "undefined" }
  else if ( typeof(object) == "boolean" || 
            typeof(object) == "number" ) {    pretty += object.toString() } 
  else if ( typeof(object) == "string" ) {    pretty += "\"" + object + "\"" } 
  else if (        object  == null) {         pretty += "null" } 
  else if ( object instanceof(Array) ) {
    if ( object.length > 0 ) {
      if (embedded) { newline = true }
      var content = ""
      for each (var item in object) { content += pp(item, depth+1) + ",\n" + spacer(depth+1) }
      content = content.replace(/,\n\s*$/, "").replace(/^\s*/,"")
      pretty += "[ " + content + "\n" + spacer(depth) + "]"
    } else { pretty += "[]" }
  } 
  else if (typeof(object) == "object") {
    if ( Object.keys(object).length > 0 ){
      if (embedded) { newline = true }
      var content = ""
      for (var key in object) { 
        content += spacer(depth + 1) + key.toString() + ": " + pp(object[key], depth+2, true) + ",\n" 
      }
      content = content.replace(/,\n\s*$/, "").replace(/^\s*/,"")
      pretty += "{ " + content + "\n" + spacer(depth) + "}"
    } else { pretty += "{}"}
  }
  else { pretty += object.toString() }
  return ((newline ? "\n" + spacer(depth) : "") + pretty)
}

A saída é assim:

js> pp({foo:"bar", baz: 1})
{ foo: "bar",
  baz: 1
}
js> var taco
js> pp({foo:"bar", baz: [1,"taco",{"blarg": "moo", "mine": "craft"}, null, taco, {}], bleep: {a:null, b:taco, c: []}})
{ foo: "bar",
  baz: 
    [ 1,
      "taco",
      { blarg: "moo",
        mine: "craft"
      },
      null,
      undefined,
      {}
    ],
  bleep: 
    { a: null,
      b: undefined,
      c: []
    }
}

Também o publiquei como um Gist aqui para quaisquer alterações futuras que possam ser necessárias.

knowtheory
fonte
7
Pode ser uma impressora bonita, mas o código não realmente olhar muito bonito :)
Xion
3

jsDump

jsDump.parse([
    window,
    document,
    { a : 5, '1' : 'foo' },
    /^[ab]+$/g,
    new RegExp('x(.*?)z','ig'),
    alert, 
    function fn( x, y, z ){
        return x + y; 
    },
    true,
    undefined,
    null,
    new Date(),
    document.body,
    document.getElementById('links')
])

torna-se

[
   [Window],
   [Document],
   {
      "1": "foo",
      "a": 5
   },
   /^[ab]+$/g,
   /x(.*?)z/gi,
   function alert( a ){
      [code]
   },
   function fn( a, b, c ){
      [code]
   },
   true,
   undefined,
   null,
   "Fri Feb 19 2010 00:49:45 GMT+0300 (MSK)",
   <body id="body" class="node"></body>,
   <div id="links">
]

QUnit (estrutura de teste de unidade usada pelo jQuery) usando a versão ligeiramente corrigida do jsDump.


JSON.stringify () não é a melhor opção em alguns casos.

JSON.stringify({f:function(){}}) // "{}"
JSON.stringify(document.body)    // TypeError: Converting circular structure to JSON
NVI
fonte
2

Assumindo a liderança de PhiLho (muito obrigado :)), acabei escrevendo a minha, pois não conseguia que ele fizesse o que queria. É bem difícil e pronto, mas faz o trabalho que eu preciso. Obrigado a todos pelas excelentes sugestões.

Não é um código brilhante, eu sei, mas pelo que vale a pena, aqui está. Alguém pode achar útil:

// Usage: dump(object)
function dump(object, pad){
    var indent = '\t'
    if (!pad) pad = ''
    var out = ''
    if (object.constructor == Array){
        out += '[\n'
        for (var i=0; i<object.length; i++){
            out += pad + indent + dump(object[i], pad + indent) + '\n'
        }
        out += pad + ']'
    }else if (object.constructor == Object){
        out += '{\n'
        for (var i in object){
            out += pad + indent + i + ': ' + dump(object[i], pad + indent) + '\n'
        }
        out += pad + '}'
    }else{
        out += object
    }
    return out
}
Dan
fonte
1
A propósito, mesmo que você possa, não deve terminar as linhas sem ponto e vírgula. Além disso, a maneira padrão de fazer __ se (! Pad) pad = '' __ seria: __ pad = (pad || '') __
Jason Bunting
Eu entendo seu argumento se (! Foo) foo = ... vs foo = (foo || ...), mas qual é a lógica para terminar todas as linhas com ponto e vírgula?
Dan
1
Você encontrará algumas idiossincrasias desagradáveis ​​do idioma, se não o fizer, sem mencionar que não será capaz de minificar seu código com facilidade (a menos que o minificador que você usa seja bom o suficiente para colocar ponto e vírgula). Consulte stackoverflow.com/questions/42247 para obter mais detalhes.
Jason Bunting
1
if (! pad) pad = ''; é mais barato, mais flexível e mais legível que pad = (pad || ''); embora por uma quantidade minuciosa. Se você insistir nesse formulário, remova os parênteses estranhos. pad = pad || ''; 3 razões para ponto-e-vírgula: JS insere automaticamente ponto-e-vírgula de linha final quando vê que a sua omissão geraria um erro. 1) Isso é forçosamente um pouco mais lento do que adicioná-lo você mesmo, e 2) pode levar a erros quando a próxima linha acontece e não gerar um erro quando combinados. 3) impedirá que seu código seja compactado.
SamGoody
1

Este é realmente apenas um comentário sobre "Use JSON.stringify de Crockford", mas não pude adicionar um comentário a essa resposta.

Conforme observado nos comentários, o JSON.stringify não funciona bem com a biblioteca Prototype (www.prototypejs.org). No entanto, é bastante fácil fazê-los funcionar bem juntos, removendo temporariamente o método Array.prototype.toJSON que o protótipo adiciona, execute o stringify () de Crockford e coloque-o novamente da seguinte forma:

  var temp = Array.prototype.toJSON;
  delete Array.prototype.toJSON;
  $('result').value += JSON.stringify(profile_base, null, 2);
  Array.prototype.toJSON = temp;
Peter Rust
fonte
1

Eu pensei que a resposta do J. Buntings ao usar o JSON.stringify também era boa. Além disso, você pode usar JSON.stringify via YUIs, objeto JSON, se estiver usando YUI. No meu caso, eu precisava despejar para HTML, para que fosse mais fácil ajustar / cortar / colar a resposta do PhiLho.

function dumpObject(obj, indent) 
{
  var CR = "<br />", SPC = "&nbsp;&nbsp;&nbsp;&nbsp;", result = "";
  if (indent == null) indent = "";

  for (var property in obj)
  {
    var value = obj[property];

    if (typeof value == 'string')
    {
      value = "'" + value + "'";
    }
    else if (typeof value == 'object')
    {
      if (value instanceof Array)
      {
        // Just let JS convert the Array to a string!
        value = "[ " + value + " ]";
      }
      else
      {
        var od = dumpObject(value, indent + SPC);
        value = CR + indent + "{" + CR + od + CR + indent + "}";
      }
    }
    result += indent + "'" + property + "' : " + value + "," + CR;
  }
  return result;
}
GTM
fonte
1

Muitas pessoas escrevem código neste tópico, com muitos comentários sobre várias dicas. Gostei desta solução porque parecia completa e era um único arquivo sem dependências.

navegador

nodejs

Ele funcionou "pronto para uso" e possui versões de nó e navegador (presumivelmente apenas invólucros diferentes, mas não procurei confirmar).

A biblioteca também suporta bastante impressão XML, SQL e CSS, mas eu não tentei esses recursos.

mm2001
fonte
0

Simples para imprimir os elementos como seqüências de caracteres:

var s = "";
var len = array.length;
var lenMinus1 = len - 1
for (var i = 0; i < len; i++) {
   s += array[i];
   if(i < lenMinus1)  {
      s += ", ";
   }
}
alert(s);
aliteralmind
fonte
0

Minha biblioteca NeatJSON possui as versões Ruby e JavaScript . Está disponível gratuitamente sob uma licença (permissiva) do MIT. Você pode ver uma demonstração / conversor on-line em:
http://phrogz.net/JS/neatjson/neatjson.html

Alguns recursos (todos opcionais):

  • Enrole para uma largura específica; se um objeto ou matriz puder caber na linha, ele será mantido em uma linha.
  • Alinhe os dois pontos de todas as chaves em um objeto.
  • Classifique as chaves para um objeto em ordem alfabética.
  • Formate números de ponto flutuante para um número específico de casas decimais.
  • Ao quebrar, use uma versão 'curta' que coloque os colchetes de abrir / fechar para matrizes e objetos na mesma linha que o primeiro / último valor.
  • Controle o espaço em branco para matrizes e objetos de maneira granular (colchetes, antes / depois de dois pontos e vírgulas).
  • Funciona no navegador da web e como um módulo Node.js.
Phrogz
fonte
-5

O flexjson inclui uma função prettyPrint () que pode fornecer o que você deseja.

andy
fonte