Formattazione dati JavaScript / stampante carina


124

Sto cercando di trovare un modo per pretty printuna struttura di dati JavaScript in un formato leggibile dall'uomo per il debug.

Ho una struttura di dati piuttosto grande e complicata che viene archiviata in JS e ho bisogno di scrivere del codice per manipolarlo. Per capire cosa sto facendo e dove sto sbagliando, ciò di cui ho davvero bisogno è poter vedere la struttura dei dati nella sua interezza e aggiornarla ogni volta che apporto modifiche attraverso l'interfaccia utente.

Tutto ciò che posso gestire da solo, oltre a trovare un modo carino per scaricare una struttura di dati JavaScript su una stringa leggibile dall'uomo. JSON lo farebbe, ma ha davvero bisogno di essere ben formattato e rientrato. Di solito userei l'eccellente materiale di dumping DOM di Firebug per questo, ma ho davvero bisogno di essere in grado di vedere l'intera struttura in una volta, il che non sembra essere possibile in Firebug.

Eventuali suggerimenti sono ben accetti

Grazie in anticipo.


Non sono sicuro di ricevere notifiche sulle modifiche delle risposte. Quindi scrivo questo commento per informarti che ho aggiunto la mia versione di dump rientrato. :-)
PhiLho,

Nota: la risposta JSON.stringify () sembra essere abbastanza utile, sebbene non sia accettata come "la" risposta.
GuruM

È possibile ottenere un output visivo e intuitivo di oggetti utilizzando nodedump: github.com/ragamufin/nodedump
ragamufin,

Risposte:


31

Ho scritto una funzione per scaricare un oggetto JS in una forma leggibile, anche se l'output non è rientrato, ma non dovrebbe essere troppo difficile aggiungerlo: ho creato questa funzione da una che ho creato per Lua (che è molto più complessa ) che ha gestito questo problema di rientro.

Ecco la versione "semplice":

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;
}

Proverò a migliorarlo un po '.
Nota 1: per usarlo, fare od = DumpObject(something)e usare od.dump. Contorto perché volevo anche il valore len (numero di elementi) per un altro scopo. È banale fare in modo che la funzione restituisca solo la stringa.
Nota 2: non gestisce i loop nei riferimenti.

MODIFICARE

Ho realizzato la versione rientrata.

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$/, "");
}

Scegli il rientro sulla linea con la chiamata ricorsiva e rinforza lo stile cambiando la linea commentata dopo questa.

... Vedo che hai montato la tua versione, il che è buono. I visitatori avranno una scelta.


1
Mi piace;) Non riesco a farlo funzionare correttamente, ma se non ti dispiace, ruberò spudoratamente il concetto e scriverò il mio :)
Dan

2
Una breve conclusione di questo approccio (rispetto al metodo JSON.stringify suggerito da Jason) è che non mostra correttamente le matrici di oggetti. Quando si dispone di una matrice di oggetti, viene visualizzato come [oggetto oggetto].
Ryan,

@Ryan: Intendi gli oggetti nativi del browser? Sì, guardando indietro al mio codice, ho visto che ho aggiunto un commento: // Peccato se un campo è un oggetto ... :-P OK per il mio test qui ... Va bene scaricare strutture create dall'utente. Vedo che ci sono alternative di seguito, se hai bisogno di qualcosa di più robusto.
PhiLho,

Non posso usare questo. Ottengo un ciclo infinito quando provo a scaricare alcuni dati json.
neoneye,

1
@RaphaelDDL & PhiLho - La dimensione massima dello stack di chiamate potrebbe essere attivata anche su un oggetto piccolo; uno con un riferimento di proprietà a se stesso. Tale riferimento provocherebbe un ciclo infinito con questa funzione.
skibulk,

233

Usa JSON.stringify di Crockford in questo modo:

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...

La variabile textsarebbe simile a questa:

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

A proposito, questo non richiede altro che quel file JS: funzionerà con qualsiasi libreria, ecc.


5
Questa è quasi sicuramente la migliore risposta che otterrai. Ho insegnato a 4 o 5 non programmatori a leggere e modificare le strutture di dati JSON.stringified e usarle ampiamente per i file di configurazione.
Joel Anair,

1
Strano che ciò causerebbe problemi: introduce il nome "JSON" nello spazio dei nomi globale, quindi ciò potrebbe causare problemi. Controlla il tuo spazio dei nomi per "JSON" prima di aggiungere questo per vedere se esiste una collisione.
Jason Bunting,

1
Bene, il prototipo è così malvagio ...;)
Jason Bunting,

7
Un aggiornamento su questo, con Firefox 3.5 e versioni successive, JSON.stringify è integrato. ( developer.mozilla.org/En/Using_JSON_in_Firefox ), quindi se stai solo cercando di vedere un oggetto JSON per scopi di debug, puoi farlo senza ulteriori dipendenze JS.
Greg Bernhardt,

3
Anche in Chrome. Tuttavia, JSON.stringify non riesce sui dati circolari JSON.stringify((function(){var x = []; x.push(x); return x})())e su molti altri tipi di oggetti JSON.stringify(/foo/).
Kragen Javier Sitaker

21

È possibile utilizzare quanto segue

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

15

In Firebug, se solo console.debug ("%o", my_object)puoi fare clic su di esso nella console ed entrare in un esploratore di oggetti interattivo. Mostra l'intero oggetto e consente di espandere gli oggetti nidificati.


1
Il problema è che mostra solo l'oggetto "più in alto": ho dozzine di oggetti nidificati e ho davvero bisogno di essere in grado di vedere l'intero contenuto in una sola volta e, soprattutto, vedere dove stanno cambiando le cose. Quindi Firebug non funziona davvero per me in questo caso.
Dan,

(sì, lo so che puoi fare clic per espanderli, ma facendo clic su circa 10 collegamenti ogni volta che voglio scaricare i dati è quello che sto facendo ora - progressi molto lenti)
Dan

1
Questo funziona anche in Chrome (e quindi presumibilmente in Safari).
Kragen Javier Sitaker


9

Per coloro che cercano un modo fantastico per vedere il tuo oggetto, controlla prettyPrint.js

Crea una tabella con opzioni di visualizzazione configurabili da stampare da qualche parte nel tuo documento. Meglio guardare che nel console.

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

inserisci qui la descrizione dell'immagine


6

Sto programmando Rhinoe non ero soddisfatto delle risposte pubblicate qui. Quindi ho scritto la mia bella stampante:

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)
}

L'output è simile al seguente:

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: []
    }
}

L'ho anche pubblicato come Gist qui per qualsiasi modifica futura possa essere richiesta.


7
Potrebbe essere una bella stampante, ma il codice in realtà non sembra molto carino :)
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')
])

diventa

[
   [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 (Framework di unit test usato da jQuery) usando la versione leggermente patchata di jsDump.


JSON.stringify () non è la scelta migliore in alcuni casi.

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

2

Prendendo la guida di PhiLho (grazie mille :)), ho finito per scrivere il mio perché non riuscivo proprio a fargli fare quello che volevo. È piuttosto grezzo e pronto, ma fa il lavoro di cui ho bisogno. Grazie a tutti per gli ottimi suggerimenti.

Non è un codice brillante, lo so, ma per quello che vale, eccolo qui. Qualcuno potrebbe trovarlo utile:

// 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
}

1
A proposito, anche se puoi, non dovresti terminare le linee senza un punto e virgola. Inoltre, il modo standard di eseguire __ if (! Pad) pad = '' __ sarebbe: __ pad = (pad || '') __
Jason Bunting,

Prendo in considerazione se (! Foo) foo = ... vs foo = (foo || ...), ma qual è la logica per terminare tutte le righe con punto e virgola?
Dan,

1
Se non lo fai, ti imbatterai in alcune brutte idiosincrasie della lingua, per non parlare del fatto che non sarai in grado di minimizzare facilmente il tuo codice (a meno che il minificatore che ti capita di usare non sia abbastanza carino da contenere punti e virgola). Consulta stackoverflow.com/questions/42247 per maggiori dettagli.
Jason Bunting,

1
if (! pad) pad = ''; è più economico, più flessibile e più leggibile rispetto a pad = (pad || ''); anche se di un importo minuto. Se insisti su quel modulo, rimuovi la parentesi estranea. pad = pad || ''; 3 motivi per i punti e virgola: JS inserisce automaticamente i punti e virgola quando rileva che ometterli genererebbe un errore. 1) Questo è un po 'più lento rispetto all'aggiunta da solo, e 2) può portare a errori quando capita la riga successiva di non generare un errore quando combinato. 3) impedirà la minimizzazione del codice.
SamGoody,

1

Questo è davvero solo un commento su "Usa Crockford's JSON.stringify" di Jason Bunting, ma non sono stato in grado di aggiungere un commento a quella risposta.

Come notato nei commenti, JSON.stringify non gioca bene con la libreria Prototype (www.prototypejs.org). Tuttavia, è abbastanza facile farli giocare bene insieme rimuovendo temporaneamente il metodo Array.prototype.toJSON che il prototipo aggiunge, esegui stringify di Crockford (), quindi rimettilo in questo modo:

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

1

Ho pensato che anche la risposta di J. Buntings sull'uso di JSON.stringify fosse buona. A parte, puoi usare JSON.stringify tramite l'oggetto JSON di YUIs se ti capita di usare YUI. Nel mio caso avevo bisogno di scaricare in HTML, quindi era più semplice modificare / tagliare / incollare la risposta 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;
}

1

Molte persone scrivono codice in questo thread, con molti commenti su vari gotcha. Mi è piaciuta questa soluzione perché sembrava completa ed era un singolo file senza dipendenze.

del browser

nodejs

Ha funzionato "out of the box" e ha entrambe le versioni del nodo e del browser (presumibilmente solo wrapper diversi ma non ho scavato per confermare).

La libreria supporta anche piuttosto la stampa di XML, SQL e CSS, ma non ho provato quelle funzionalità.


0

Un semplice per stampare gli elementi come stringhe:

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);

0

La mia libreria NeatJSON ha entrambe le versioni Ruby e JavaScript . È liberamente disponibile con una Licenza MIT (permissiva). È possibile visualizzare una demo / convertitore online su:
http://phrogz.net/JS/neatjson/neatjson.html

Alcune funzionalità (tutte opzionali):

  • Avvolgere ad una larghezza specifica; se un oggetto o un array può adattarsi alla linea, viene mantenuto su una linea.
  • Allinea i due punti per tutte le chiavi di un oggetto.
  • Ordina le chiavi di un oggetto in ordine alfabetico.
  • Formattare i numeri in virgola mobile su un numero specifico di decimali.
  • Quando si esegue il wrapping, utilizzare una versione 'corta' che mette le parentesi aperte / chiuse per matrici e oggetti sulla stessa riga del primo / ultimo valore.
  • Controlla lo spazio bianco per matrici e oggetti in modo granulare (parentesi interne, due punti prima / dopo e virgole).
  • Funziona nel browser Web e come modulo Node.js.

-5

flexjson include una funzione prettyPrint () che potrebbe darti quello che vuoi.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.