Formateo de datos JavaScript / impresora bonita


124

Estoy tratando de encontrar un camino hacia pretty printuna estructura de datos JavaScript en una forma legible para la depuración.

Tengo una estructura de datos bastante grande y complicada que se almacena en JS y necesito escribir un código para manipularlo. Para resolver lo que estoy haciendo y dónde me estoy equivocando, lo que realmente necesito es poder ver la estructura de datos en su totalidad y actualizarla cada vez que realice cambios a través de la interfaz de usuario.

Todo esto lo puedo manejar yo mismo, además de encontrar una buena manera de volcar una estructura de datos JavaScript en una cadena legible por humanos. JSON lo haría, pero realmente necesita estar bien formateado y sangrado. Por lo general, usaría el excelente material de descarga DOM de Firebug para esto, pero realmente necesito poder ver toda la estructura a la vez, lo que no parece posible en Firebug.

Cualquier sugerencia es bienvenida.

Gracias por adelantado.


No estoy seguro si se le notifican las ediciones de las respuestas. Entonces escribo este comentario para informarle que agregué mi propia versión de volcado sangrado. :-)
PhiLho

Nota: La respuesta JSON.stringify () parece ser bastante útil, aunque no se acepta como 'la' respuesta.
GuruM

Puede obtener una salida visual e intuitiva de objetos usando nodedump: github.com/ragamufin/nodedump
ragamufin

Respuestas:


31

Escribí una función para volcar un objeto JS en una forma legible, aunque el resultado no está sangrado, pero no debería ser demasiado difícil agregar eso: hice esta función desde una que hice para Lua (que es mucho más complejo ) que manejó este problema de sangría.

Aquí está la versión "simple":

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

Trataré de mejorarlo un poco.
Nota 1: Para usarlo, haz od = DumpObject(something)y usa od.dump. Enrevesado porque también quería el valor de len (número de artículos) para otro propósito. Es trivial hacer que la función devuelva solo la cadena.
Nota 2: no maneja bucles en las referencias.

EDITAR

Hice la versión con sangría.

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

Elija su sangría en la línea con la llamada recursiva, y refuerza el estilo cambiando la línea comentada después de esta.

... Veo que preparaste tu propia versión, lo cual es bueno. Los visitantes tendrán una opción.


1
Me gusta;) No puedo hacer que funcione correctamente, pero si no te importa, voy a robar descaradamente el concepto y escribir el mío :)
Dan

2
Un pequeño inconveniente de este enfoque (en comparación con el método JSON.stringify que Jason sugiere) es que no muestra las matrices de objetos correctamente. Cuando tiene una matriz de objetos, aparece como [objeto Object].
Ryan

@ Ryan: ¿Te refieres a los objetos nativos del navegador? Sí, volviendo a mirar mi código, vi que agregué un comentario: // Lástima que un campo sea un objeto ... :-P OK para mi prueba aquí ... Está bien volcar estructuras hechas por el usuario. Veo que hay alternativas a continuación, si necesita algo más robusto.
PhiLho

No puedo usar esto. Obtengo un bucle infinito cuando intento volcar algunos datos json.
neoneye

1
@RaphaelDDL & PhiLho: el tamaño máximo de la pila de llamadas también podría activarse en un objeto pequeño; uno con una referencia de propiedad a sí mismo. Tal referencia causaría un bucle infinito con esta función.
skibulk

233

Use Crockford's JSON.stringify de esta manera:

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 variable textse vería así:

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

Por cierto, esto no requiere nada más que ese archivo JS: funcionará con cualquier biblioteca, etc.


55
Esta es casi definitivamente la mejor respuesta que obtendrá. He enseñado a 4 o 5 no programadores a leer y editar estructuras de datos JSON.stringified y usarlas ampliamente para archivos de configuración.
Joel Anair

1
Es extraño que cause problemas: introduce el nombre "JSON" en el espacio de nombres global, por lo que puede causarle problemas. Verifique su espacio de nombres para "JSON" antes de agregar esto para ver si existe una colisión.
Jason Bunting

1
Bueno, el prototipo es malvado así ...;)
Jason Bunting

77
Una actualización sobre esto, con Firefox 3.5 y superior, JSON.stringify está integrado. ( developer.mozilla.org/En/Using_JSON_in_Firefox ), por lo que si solo está tratando de ver un objeto JSON con fines de depuración, puede hacerlo sin dependencias JS adicionales.
Greg Bernhardt

3
También en cromo. Sin embargo, JSON.stringify falla en datos circulares JSON.stringify((function(){var x = []; x.push(x); return x})())y en muchos otros tipos de objetos JSON.stringify(/foo/).
Kragen Javier Sitaker

21

Puedes usar lo siguiente

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

15

En Firebug, si solo console.debug ("%o", my_object)puede hacer clic en él en la consola e ingresar a un explorador de objetos interactivo. Muestra todo el objeto y le permite expandir objetos anidados.


1
El problema con eso es que solo muestra el objeto 'más alto': tengo docenas de objetos anidados, y realmente necesito poder ver todo el contenido a la vez, y lo que es más importante, ver dónde están cambiando las cosas. Entonces Firebug realmente no está funcionando para mí en este caso.
Dan

(sí, sé que puede hacer clic para expandirlos, pero hacer clic en más o menos 10 enlaces cada vez que quiero volcar los datos es lo que estoy haciendo ahora - progreso muy lento)
Dan

1
Esto también funciona en Chrome (y, por lo tanto, presumiblemente en Safari).
Kragen Javier Sitaker


9

Para aquellos que buscan una forma increíble de ver su objeto, revisen prettyPrint.js

Crea una tabla con opciones de vista configurables para imprimir en algún lugar de su documento. Mejor mirar que en el console.

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

ingrese la descripción de la imagen aquí


6

Estoy programando Rhinoy no estaba satisfecho con ninguna de las respuestas que se publicaron aquí. Así que escribí mi propia impresora 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)
}

El resultado se ve así:

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

También lo publiqué aquí como un Gist para cualquier cambio futuro que se requiera.


77
Puede ser una impresora bonita, pero el código en realidad no se ve muy 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')
])

se convierte

[
   [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 (marco de prueba de unidad utilizado por jQuery) usando una versión ligeramente parcheada de jsDump.


JSON.stringify () no es la mejor opción en algunos casos.

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

2

Tomando el liderazgo de PhiLho (muchas gracias :)), terminé escribiendo el mío ya que no podía lograr que él hiciera lo que quería. Es bastante duro y listo, pero hace el trabajo que necesito. Gracias a todos por las excelentes sugerencias.

No es un código brillante, lo sé, pero por lo que vale, aquí está. Alguien puede encontrarlo ú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
}

1
Por cierto, aunque pueda, no debe terminar las líneas sin punto y coma. Además, la forma estándar de hacer __ if (! Pad) pad = '' __ sería: __ pad = (pad || '') __
Jason Bunting

Tomo su punto sobre if (! Foo) foo = ... vs foo = (foo || ...), pero ¿cuál es la razón para terminar todas las líneas con punto y coma?
Dan

1
Si no lo hace, se encontrará con algunas idiosincrasias desagradables del idioma, sin mencionar que no podrá minificar fácilmente su código (a menos que el minificador que utilice sea lo suficientemente bueno como para ponerle punto y coma). Consulte stackoverflow.com/questions/42247 para obtener más detalles.
Jason Bunting

1
if (! pad) pad = ''; es más barato, más flexible y más legible que pad = (pad || ''); aunque por un minuto. Si insiste en esa forma, elimine el paréntesis extraño. pad = pad || ''; 3 razones para el punto y coma: JS inserta automáticamente los puntos y comas de la línea final cuando ve que omitirlos arrojaría un error. 1) Esto es forzosamente un poco más lento que agregarlo usted mismo, y 2) puede provocar errores cuando la siguiente línea no arroje un error cuando se combina. 3) evitará que su código sea minimizado.
SamGoody

1

Esto es realmente solo un comentario sobre "Use JSON.stringify" de Jason Bunting, pero no pude agregar un comentario a esa respuesta.

Como se señaló en los comentarios, JSON.stringify no funciona bien con la biblioteca Prototype (www.prototypejs.org). Sin embargo, es bastante fácil hacer que jueguen bien juntos eliminando temporalmente el método Array.prototype.toJSON que agrega el prototipo, ejecute el stringify () de Crockford y luego vuelva a colocarlo así:

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

1

Pensé que la respuesta de J. Buntings sobre el uso de JSON.stringify también fue buena. Además, puede usar JSON.stringify a través del objeto JSON de YUI si está usando YUI. En mi caso, necesitaba volcar a HTML, por lo que fue más fácil simplemente ajustar / cortar / pegar la respuesta de 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

Mucha gente escribe código en este hilo, con muchos comentarios sobre varios problemas. Me gustó esta solución porque parecía completa y era un solo archivo sin dependencias.

navegador

nodejs

Funcionó "fuera de la caja" y tiene versiones tanto de nodo como de navegador (presumiblemente solo envoltorios diferentes pero no busqué para confirmar).

La biblioteca también admite la impresión bonita de XML, SQL y CSS, pero no he probado esas características.


0

Una simple para imprimir los elementos como cadenas:

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

Mi biblioteca NeatJSON tiene versiones de Ruby y JavaScript . Está disponible gratuitamente bajo una Licencia MIT (permisiva). Puede ver una demostración / convertidor en línea en:
http://phrogz.net/JS/neatjson/neatjson.html

Algunas características (todas opcionales):

  • Ajustar a un ancho específico; Si un objeto o matriz puede caber en la línea, se mantiene en una línea.
  • Alinee los dos puntos para todas las claves en un objeto.
  • Ordenar las claves de un objeto alfabéticamente.
  • Formatee números de coma flotante a un número específico de decimales.
  • Al ajustar, use una versión 'corta' que ponga los corchetes de apertura / cierre para matrices y objetos en la misma línea que el primer / último valor.
  • Controle el espacio en blanco para las matrices y los objetos de forma granular (dentro de corchetes, antes y después de dos puntos y comas).
  • Funciona en el navegador web y como un módulo Node.js.

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.