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

Dan
źródło
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
Zajrzyj tam: stackoverflow.com/questions/4810841/…
monkeythedev

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.

PhiLho
źródło
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.

Jason Bunting
źródło
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>
Dharmanshu Kamra
źródło
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.

John Millikin
źródło
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

RaphaelDDL
źródło
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.

wiedzy
źródło
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
NVI
źródło
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
}
Dan
źródło
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;
Peter Rust
źródło
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;
}
GTM
źródło
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.

mm2001
źródło
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);
aliteralmind
źródło
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.
Phrogz
źródło
-5

flexjson zawiera funkcję prettyPrint (), która może dać ci to, czego chcesz.

andy
źródło