Форматирование данных JavaScript / красивый принтер


124

Я пытаюсь найти способ сделать pretty printструктуру данных JavaScript в удобочитаемой форме для отладки.

У меня довольно большая и сложная структура данных, хранящаяся в JS, и мне нужно написать код для управления ею. Чтобы понять, что я делаю и где ошибаюсь, мне действительно нужно иметь возможность видеть структуру данных во всей ее полноте и обновлять ее всякий раз, когда я вношу изменения через пользовательский интерфейс.

Со всем этим я могу справиться сам, за исключением того, что нашел хороший способ вывести структуру данных JavaScript в удобочитаемую строку. JSON подойдет, но он действительно должен быть хорошо отформатирован и с отступом. Я обычно использую для этого превосходный дамп DOM от Firebug, но мне действительно нужно иметь возможность видеть всю структуру сразу, что, похоже, невозможно в Firebug.

Любые предложения приветствуются.

Заранее спасибо.


Не уверен, что вас уведомляют об изменении ответов. Поэтому я пишу этот комментарий, чтобы сообщить вам, что я добавил свою версию дампа с отступом. :-)
PhiLho

Примечание. Ответ JSON.stringify () кажется весьма полезным, хотя он не принимается как «ответ».
GuruM

Вы можете получить визуальный и интуитивно понятный вывод объектов с помощью nodedump: github.com/ragamufin/nodedump
ragamufin 03

Ответы:


31

Я написал функцию для вывода JS-объекта в читаемой форме, хотя вывод не имеет отступа, но это не должно быть слишком сложно добавить: я сделал эту функцию из той, которую я сделал для Lua (которая намного сложнее ), которая решила эту проблему с отступом.

Вот «простая» версия:

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

Я посмотрю, как его немного улучшить.
Примечание 1. Чтобы использовать это, сделайте od = DumpObject(something)и используйте od.dump. Сложно, потому что мне нужно было значение len (количество элементов) для другой цели. Заставить функцию возвращать только строку - тривиально.
Примечание 2: он не обрабатывает циклы в ссылках.

РЕДАКТИРОВАТЬ

Я сделал версию с отступом.

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

Выберите отступ в строке с рекурсивным вызовом и закрепите стиль, переключив прокомментированную строку после этой.

... Я вижу, вы придумали свою версию, и это хорошо. У посетителей будет выбор.


1
Мне нравится;) Не могу заставить его работать должным образом, но, если вы не возражаете, я без зазрения совести украду концепцию и напишу свой собственный :)
Дэн

2
Одним из недостатков этого подхода (по сравнению с методом JSON.stringify, который предлагает Джейсон) является то, что он неправильно отображает массивы объектов. Когда у вас есть массив объектов, он отображается как [объект Object].
Райан,

@Ryan: Вы имеете в виду собственные объекты браузера? Да, оглядываясь назад на свой код, я увидел, что добавил комментарий: // Плохо, если одно поле является объектом ... :-P Хорошо для моего теста здесь ... Можно сбрасывать структуры, созданные пользователем. Я вижу, что ниже есть альтернативы, если вам нужно что-то более надежное.
PhiLho

Я не могу это использовать. Я получаю бесконечный цикл, когда пытаюсь сбросить некоторые данные json.
neoneye

1
@RaphaelDDL & PhiLho - Максимальный размер стека вызовов также может быть активирован для небольшого объекта; один со ссылкой свойства на себя. Такая ссылка вызовет бесконечный цикл с этой функцией.
skibulk

233

Используйте JSON.stringify Крокфорда следующим образом:

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

Переменная textбудет выглядеть так:

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

Кстати, для этого не требуется ничего, кроме этого JS-файла - он будет работать с любой библиотекой и т. Д.


5
Это почти наверняка лучший ответ, который вы получите. Я научил 4 или 5 непрограммистов читать и редактировать структуры данных JSON.stringified и широко использовать их для файлов конфигурации.
Джоэл Анаир,

1
Странно, что это может вызвать проблемы - он вводит имя «JSON» в глобальное пространство имен, так что это может вызвать у вас проблемы. Перед добавлением проверьте свое пространство имен на наличие «JSON», чтобы увидеть, существует ли коллизия.
Джейсон Бантинг,

1
Ну, прототип - это зло ...;)
Джейсон Бантинг,

7
Обновление по этому поводу, с Firefox 3.5 и выше, JSON.stringify является встроенным. ( developer.mozilla.org/En/Using_JSON_in_Firefox ), поэтому, если вы просто пытаетесь увидеть объект JSON для целей отладки, вы можете сделать это без дополнительных зависимостей JS.
Грег Бернхардт

3
Также в Chrome. Однако JSON.stringify не работает с циклическими данными JSON.stringify((function(){var x = []; x.push(x); return x})())и многими другими объектами JSON.stringify(/foo/).
Kragen Javier Sitaker


15

В Firebugслучае, если вы просто console.debug ("%o", my_object)можете щелкнуть по нему в консоли и войти в интерактивный проводник объектов. Он показывает весь объект и позволяет раскрывать вложенные объекты.


1
Проблема в том, что он показывает только «самый верхний» объект - у меня есть десятки вложенных объектов, и мне действительно нужно иметь возможность видеть все содержимое сразу, и, что важно, видеть, где что-то меняется. Так что Firebug в данном случае для меня не работает.
Дэн

(да, я знаю, что вы можете щелкнуть, чтобы развернуть их, но щелкая около 10 ссылок каждый раз, когда я хочу выгрузить данные, я делаю это сейчас - очень медленный прогресс)
Дэн

1
Это также работает в Chrome (и, следовательно, предположительно в Safari).
Kragen Javier Sitaker


9

Для тех, кто ищет отличный способ увидеть свой объект, проверьте prettyPrint.js

Создает таблицу с настраиваемыми параметрами просмотра для печати где-нибудь в вашем документе. Лучше посмотреть, чем в console.

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

введите описание изображения здесь


6

Я программирую, Rhinoи меня не удовлетворил ни один из ответов, опубликованных здесь. Итак, я написал свой собственный симпатичный принтер:

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

Результат выглядит так:

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

Я также разместил его здесь как Gist для любых будущих изменений, которые могут потребоваться.


7
Это может быть симпатичный принтер, но код на самом деле выглядит не очень красиво :)
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')
])

становится

[
   [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 ( фреймворк модульного тестирования, используемый jQuery) с использованием слегка исправленной версии jsDump.


JSON.stringify () - не лучший выбор в некоторых случаях.

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

2

Взяв на себя инициативу PhiLho (большое спасибо :)), я написал свой собственный, поскольку не мог заставить его делать то, что я хотел. Он довольно грубый и готовый, но он делает то, что мне нужно. Спасибо всем за отличные предложения.

Я знаю, что это не блестящий код, но, как бы то ни было, вот он. Кому-то это может пригодиться:

// 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
Кстати, даже если вы можете, вы не должны заканчивать строки без точки с запятой. Кроме того, стандартный способ выполнения __ if (! Pad) pad = '' __ будет выглядеть следующим образом: __ pad = (pad || '') __
Джейсон Бантинг

Я понимаю вашу точку зрения на if (! Foo) foo = ... vs foo = (foo || ...), но каково основание для завершения всех строк точкой с запятой?
Дэн

1
Если вы этого не сделаете, вы столкнетесь с некоторыми неприятными особенностями языка, не говоря уже о том, что вы не сможете легко минимизировать свой код (если только минификатор, который вы используете, не достаточно хорош, чтобы вставить точку с запятой). См. Stackoverflow.com/questions/42247 для получения дополнительной информации.
Джейсон Бантинг,

1
если (! pad) pad = ''; дешевле, гибче и читабельнее, чем pad = (pad || ''); хоть и на минутную сумму. Если вы настаиваете на этой форме, удалите лишние скобки. pad = pad || ''; 3 причины использования точек с запятой: JS автоматически вставляет точки с запятой в конце строки, когда видит, что их пропуск приведет к ошибке. 1) Это принудительно немного медленнее, чем добавление самостоятельно, и 2) может привести к ошибкам, когда следующая строка не выдаст ошибку при объединении. 3) предотвратит минимизацию вашего кода.
SamGoody

1

На самом деле это просто комментарий к статье Джейсона Бантинга «Использовать JSON.stringify Крокфорда», но я не смог добавить комментарий к этому ответу.

Как отмечено в комментариях, JSON.stringify не очень хорошо работает с библиотекой Prototype (www.prototypejs.org). Тем не менее, довольно легко заставить их хорошо работать вместе, временно удалив метод Array.prototype.toJSON, добавленный прототипом, запустите Crockford stringify (), а затем верните его следующим образом:

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

1

Я подумал, что ответ Дж. Бантингса на использование JSON.stringify тоже был хорош. Кроме того, вы можете использовать JSON.stringify через объект YUI JSON, если вы используете YUI. В моем случае мне нужно было сделать дамп в HTML, чтобы было проще просто настроить / вырезать / вставить ответ 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

Многие люди пишут код в этой ветке, оставляя много комментариев по поводу различных ошибок. Мне понравилось это решение, потому что оно казалось законченным и представляло собой единый файл без зависимостей.

браузер

nodejs

Он работал «из коробки» и имел версии для узла и браузера (предположительно, просто разные оболочки, но я не стал копаться, чтобы подтвердить).

Библиотека также поддерживает удобную печать XML, SQL и CSS, но я не пробовал эти функции.


0

Простой для печати элементов в виде строк:

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

Моя библиотека NeatJSON имеет версии Ruby и JavaScript . Он находится в свободном доступе по (разрешительной) лицензии MIT. Вы можете просмотреть онлайн-демонстрацию / конвертер по адресу:
http://phrogz.net/JS/neatjson/neatjson.html

Некоторые функции (все необязательно):

  • Оберните до определенной ширины; если объект или массив может уместиться в строке, он хранится в одной строке.
  • Совместите двоеточия для всех ключей в объекте.
  • Отсортируйте ключи к объекту по алфавиту.
  • Отформатируйте числа с плавающей запятой до определенного количества десятичных знаков.
  • При переносе используйте «короткую» версию, в которой открывающие / закрывающие скобки для массивов и объектов помещаются в одну строку с первым / последним значением.
  • Детально контролируйте пробелы для массивов и объектов (внутри скобок, до / после двоеточий и запятых).
  • Работает в веб-браузере и как модуль Node.js.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.