Formatowanie danych JavaScript / ładna drukarka


124

Próbuję znaleźć sposób na pretty printstrukturę danych JavaScript w postaci czytelnej dla człowieka do debugowania.

Mam dość dużą i skomplikowaną strukturę danych przechowywaną w JS i muszę napisać kod, aby nim manipulować. Aby dowiedzieć się, co robię i co robię źle, naprawdę potrzebuję możliwości zobaczenia całej struktury danych i zaktualizowania jej za każdym razem, gdy wprowadzam zmiany za pośrednictwem interfejsu użytkownika.

Z tym wszystkim mogę sobie poradzić, poza znalezieniem fajnego sposobu na zrzucenie struktury danych JavaScript do ciągu czytelnego dla człowieka. JSON by się nadał, ale naprawdę musi być ładnie sformatowany i z wcięciami. Zwykle używałbym do tego doskonałego narzędzia do dumpingu DOM Firebuga, ale naprawdę muszę być w stanie zobaczyć całą strukturę na raz, co wydaje się nie być możliwe w Firebug.

Wszelkie sugestie są mile widziane.

Z góry dziękuję.


Nie wiem, czy otrzymujesz powiadomienia o zmianie odpowiedzi. Dlatego piszę ten komentarz, aby poinformować, że dodałem własną wersję zrzutu z wcięciem. :-)
— PhiLho

Uwaga: odpowiedź JSON.stringify () wydaje się być całkiem przydatna, chociaż nie jest akceptowana jako odpowiedź „the”.
— GuruM,

Możesz uzyskać wizualne i intuicyjne wyjście obiektów za pomocą nodedump: github.com/ragamufin/nodedump
— ragamufin

Odpowiedzi:


31

Napisałem funkcję do zrzutu obiektu JS w czytelnej formie, chociaż wynik nie jest wcięty, ale nie powinno być zbyt trudne, aby to dodać: zrobiłem tę funkcję z jednej, którą stworzyłem dla Lua (która jest znacznie bardziej złożona ), który rozwiązał ten problem z wcięciami.

Oto „prosta” wersja:

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

Popatrzę, jak to poprawić.
Uwaga 1: Aby go użyć, zrób od = DumpObject(something)i użyj od.dump. Zagmatwane, ponieważ chciałem, aby wartość len (liczba elementów) też miała inny cel. Sprawienie, by funkcja zwracała tylko ciąg znaków, jest trywialne.
Uwaga 2: nie obsługuje pętli w referencjach.

EDYTOWAĆ

Zrobiłem wersję z wcięciem.

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

Wybierz wcięcie w wierszu z wywołaniem rekurencyjnym, a styl nawiasów klamrowych, przełączając skomentowaną linię po tym.

... Widzę, że stworzyłeś swoją własną wersję, co jest dobre. Zwiedzający będą mieli wybór.


1
Podoba mi się;) Nie mogę zmusić go do prawidłowego działania, ale jeśli nie masz nic przeciwko, bezwstydnie ukradnę koncepcję i napiszę własną :)
— Dan

2
Jednym z krótkich rozwiązań tego podejścia (w porównaniu z metodą JSON.stringify, którą sugeruje Jason) jest to, że nie wyświetla ono poprawnie tablic obiektów. Gdy masz tablicę obiektów, pojawia się ona jako [obiekt obiekt].
— Ryan

@Ryan: Masz na myśli natywne obiekty przeglądarki? Tak, patrząc wstecz na mój kod, zauważyłem, że dodałem komentarz: // Szkoda, jeśli jedno pole jest obiektem ... :-P OK dla mojego testu tutaj ... Można zrzucić struktury utworzone przez użytkownika. Widzę, że poniżej są alternatywy, jeśli potrzebujesz czegoś solidniejszego.
— PhiLho

Nie mogę tego użyć. Otrzymuję nieskończoną pętlę, gdy próbuję zrzucić niektóre dane json.
— neoneye

1
@RaphaelDDL & PhiLho - Maksymalny rozmiar stosu wywołań może być również wyzwalany na małym obiekcie; jeden z odniesieniem do siebie. Takie odniesienie spowodowałoby nieskończoną pętlę z tą funkcją.
— skibulk

233

Użyj JSON.stringify Crockforda w następujący sposób:

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

Zmienna textwyglądałaby następująco:

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

Nawiasem mówiąc, nie wymaga to niczego więcej niż tego pliku JS - będzie działać z każdą biblioteką itp.


5
To prawie na pewno najlepsza odpowiedź, jaką otrzymasz. Nauczyłem 4 lub 5 nie-programistów, jak czytać i edytować struktury danych JSON.stringified i używać ich w szerokim zakresie do plików konfiguracyjnych.
— Joel Anair,

1
Dziwne, że spowodowałoby to problemy - wprowadza nazwę „JSON” do globalnej przestrzeni nazw, więc może powodować problemy. Sprawdź swoją przestrzeń nazw pod kątem „JSON” przed dodaniem tego, aby zobaczyć, czy istnieje kolizja.
— Jason Bunting

1
Cóż, prototyp jest taki zły ...;)
— Jason Bunting

7
Aktualizacja w tej sprawie, w przeglądarce Firefox 3.5 i nowszych wersjach JSON.stringify jest wbudowany. ( developer.mozilla.org/En/Using_JSON_in_Firefox ), więc jeśli tylko próbujesz zobaczyć obiekt JSON do celów debugowania, możesz to zrobić bez dodatkowych zależności JS.
— Greg Bernhardt

3
Również w Chrome. Jednak JSON.stringify kończy się niepowodzeniem w przypadku danych cyklicznych JSON.stringify((function(){var x = []; x.push(x); return x})())i wielu innych rodzajów obiektów JSON.stringify(/foo/).
— Kragen Javier Sitaker

21

Możesz użyć następujących

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

15

W Firebug, jeśli tylko console.debug ("%o", my_object)możesz kliknąć na niego w konsoli i wejść do interaktywnego eksploratora obiektów. Pokazuje cały obiekt i pozwala rozwinąć zagnieżdżone obiekty.


1
Problem polega na tym, że pokazuje tylko „najwyższy” obiekt - mam dziesiątki zagnieżdżonych obiektów i naprawdę muszę być w stanie zobaczyć całą zawartość naraz, a co ważniejsze, zobaczyć, gdzie się zmieniają. Więc Firebug naprawdę nie działa dla mnie w tym przypadku.
— Dan

(tak, wiem, że możesz kliknąć, aby je rozwinąć, ale klikanie około 10 linków za każdym razem, gdy chcę zrzucić dane, jest tym, co teraz robię - bardzo powolny postęp)
— Dan,

1
Działa to również w Chrome (a zatem prawdopodobnie w Safari).
— Kragen Javier Sitaker


9

Dla tych, którzy szukają niesamowitego sposobu, aby zobaczyć swój obiekt, sprawdź prettyPrint.js

Tworzy tabelę z konfigurowalnymi opcjami widoku do wydrukowania gdzieś w dokumencie. Lepiej patrzeć niż w console.

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

wprowadź opis obrazu tutaj


6

Programuję Rhinoi nie zadowalała mnie żadna z zamieszczonych tutaj odpowiedzi. Napisałem więc własną ładną drukarkę:

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

Wynik wygląda następująco:

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

Opublikowałem to również jako podsumowanie tutaj wszelkich przyszłych zmian, które mogą być wymagane.


7
To może być ładna drukarka, ale kod tak naprawdę nie wygląda zbyt ładnie :)
— 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')
])

staje się

[
   [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 do testów jednostkowych używany przez jQuery) przy użyciu lekko poprawionej wersji jsDump.


JSON.stringify () nie jest najlepszym wyborem w niektórych przypadkach.

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

2

Idąc za przykładem PhiLho (bardzo dziękuję :)), napisałem własną, ponieważ nie mogłem zmusić go do zrobienia tego, co chciałem. Jest dość szorstki i gotowy, ale spełnia moje wymagania. Dziękuję wszystkim za doskonałe sugestie.

Wiem, że to nie jest genialny kod, ale warto. Ktoś może uznać to za przydatne:

// 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
Nawiasem mówiąc, nawet jeśli możesz, nie powinieneś kończyć wierszy bez średnika. Ponadto standardowy sposób wykonywania __ if (! Pad) pad = '' __ to: __ pad = (pad || '') __
— Jason Bunting,

Skupiam się na tym, że jeśli (! Foo) foo = ... vs foo = (foo || ...), ale jakie jest uzasadnienie kończenia wszystkich wierszy średnikami?
— Dan

1
Jeśli tego nie zrobisz, napotkasz kilka paskudnych dziwactw języka, nie wspominając o tym, że nie będziesz w stanie łatwo zminimalizować swojego kodu (chyba że minifier, którego używasz, jest na tyle fajny, że wstawia za ciebie średniki). Więcej informacji znajdziesz na stackoverflow.com/questions/42247 .
— Jason Bunting,

1
if (! pad) pad = ''; jest tańszy, bardziej elastyczny i bardziej czytelny niż pad = (pad || ''); aczkolwiek o niewielką kwotę. Jeśli nalegasz na ten formularz, usuń obcy nawias. pad = pad || ''; 3 powody stosowania średników: JS automatycznie wstawia średniki końca linii, gdy widzi, że ich pominięcie spowodowałoby błąd. 1) Jest to siłą rzeczy trochę wolniejsze niż dodawanie go samemu i 2) może prowadzić do błędów, gdy w następnej linii zdarza się, że po połączeniu nie pojawia się błąd. 3) zapobiegnie zminimalizowaniu kodu.
— SamGoody

1

To tak naprawdę tylko komentarz do artykułu Jasona Buntinga „Użyj JSON.stringify Crockforda”, ale nie byłem w stanie dodać komentarza do tej odpowiedzi.

Jak zauważono w komentarzach, JSON.stringify nie współpracuje dobrze z biblioteką Prototype (www.prototypejs.org). Jednak dość łatwo jest sprawić, że będą dobrze ze sobą współpracować, tymczasowo usuwając metodę Array.prototype.toJSON, którą dodaje prototyp, uruchom stringify () Crockforda, a następnie umieść ją z powrotem w następujący sposób:

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

1

Myślałem, że odpowiedź J. Buntings na temat używania JSON.stringify była również dobra. A na marginesie, jeśli używasz YUI, możesz użyć JSON.stringify za pośrednictwem obiektu YUI JSON. W moim przypadku musiałem zrzucić do HTML, więc łatwiej było po prostu poprawić / wyciąć / wkleić odpowiedź 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

Wiele osób piszących kod w tym wątku, z wieloma komentarzami na temat różnych problemów. Podobało mi się to rozwiązanie, ponieważ wydawało się kompletne i był to pojedynczy plik bez zależności.

przeglądarka

nodejs

Działało „po wyjęciu z pudełka” i ma wersje dla węzłów i przeglądarki (prawdopodobnie tylko różne opakowania, ale nie szukałem potwierdzenia).

Biblioteka obsługuje również ładne drukowanie XML, SQL i CSS, ale nie próbowałem tych funkcji.


0

Prosty do drukowania elementów jako ciągów:

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

Moja biblioteka NeatJSON ma wersje Ruby i JavaScript . Jest dostępny bezpłatnie na (liberalnej) licencji MIT. Możesz obejrzeć demo / konwerter online pod adresem :
http://phrogz.net/JS/neatjson/neatjson.html

Niektóre funkcje (wszystkie opcjonalne):

  • Zawiń do określonej szerokości; jeśli obiekt lub tablica zmieści się w linii, to jest trzymana w jednej linii.
  • Wyrównaj dwukropki dla wszystkich kluczy w obiekcie.
  • Posortuj klucze do obiektu alfabetycznie.
  • Sformatuj liczby zmiennoprzecinkowe do określonej liczby miejsc po przecinku.
  • Podczas zawijania użyj „krótkiej” wersji, która umieszcza nawiasy otwierające / zamykające dla tablic i obiektów w tym samym wierszu, co pierwsza / ostatnia wartość.
  • Kontroluj spacje dla tablic i obiektów w sposób szczegółowy (w nawiasach, przed / po dwukropkach i przecinkach).
  • Działa w przeglądarce internetowej oraz jako moduł Node.js.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.