Wie kann ich JSON mit Go hübsch drucken?

190

Kennt jemand eine einfache Möglichkeit, JSON-Ausgaben in Go hübsch zu drucken?

Das Standardpaket http://golang.org/pkg/encoding/json/ scheint keine Funktionalität dafür zu enthalten (BEARBEITEN: siehe akzeptierte Antwort), und eine schnelle Google-Suche zeigt nichts Offensichtliches.

Die von mir gesuchten Verwendungszwecke sind das hübsche Drucken des Ergebnisses json.Marshalund das Formatieren einer Zeichenfolge voller JSON von überall, sodass das Lesen für Debug-Zwecke einfacher ist.

Brad Peabody
quelle
Achtung: auf meine Versuche, die Saiten Indizes in JSON Wörterbücher müssen in Klammern. Also, {name: "value"}wird nicht in Ordnung sein, obwohl die meisten Javascript-Interpreter es verwenden . Funktioniert nur {"name": "value"} mit den Funktionen der Go JSON-Bibliothek.
Peterh - Wiedereinsetzung Monica
2
@peterh Ich denke, Sie verwechseln die JavaScript-Literal-Syntax mit JSON. Die JSON-Spezifikation ( json.org ) gibt deutlich an, dass nur Zeichenfolgenliterale zulässig sind (dh Anführungszeichen erforderlich sind), während die JS- Sprachobjektsyntax diese Einschränkung nicht aufweist. Die Go-Bibliothek folgt der Spezifikation.
Brad Peabody

Antworten:

295

Mit hübschem Druck meine ich, dass Sie so eingerückt meinen

{
    "data": 1234
}

eher, als

{"data":1234}

Der einfachste Weg, dies zu tun, ist mit MarshalIndent, mit dem Sie angeben können, wie es über das indentArgument eingerückt werden soll . Somit json.MarshalIndent(data, "", " ")wird mit vier Leerzeichen zum Einrücken hübsch gedruckt.

Alexander Bauer
quelle
17
Ja, das sieht genau so aus - es ist bereits eingebaut, nur noch muss das Schlüsselwort "Pretty-Print" in das pkg-Dokument aufgenommen werden, damit der nächste Suchende es findet. (Hinterlässt eine Feedback-Notiz für die Dokumentbetreuer.) Tks!
Brad Peabody
37
json.MarshalIndent(data, "", "\t")wenn Sie Registerkarten möchten.
Kyle Brandt
77
json.MarshalIndent(data, "", "🐱")wenn du Katzen willst. Entschuldigung
BriiC
43
json.MarshalIndent(data, "", "\t🐱")wenn Sie wollen ... Tabby Katzen ... Entschuldigung
Davos
78

Die akzeptierte Antwort ist großartig, wenn Sie ein Objekt haben, das Sie in JSON verwandeln möchten. In der Frage wird auch erwähnt, dass nur eine beliebige JSON-Zeichenfolge hübsch gedruckt wird, und genau das habe ich versucht. Ich wollte nur etwas JSON von einer POST-Anfrage (insbesondere einem CSP-Verstoßbericht ) hübsch protokollieren .

Um zu verwenden MarshalIndent, müssten Sie Unmarshaldas in ein Objekt. Wenn Sie das brauchen, machen Sie es, aber ich habe es nicht getan. Wenn Sie nur ein Byte-Array hübsch drucken müssen, ist plain IndentIhr Freund.

Folgendes habe ich erreicht:

import (
    "bytes"
    "encoding/json"
    "log"
    "net/http"
)

func HandleCSPViolationRequest(w http.ResponseWriter, req *http.Request) {
    body := App.MustReadBody(req, w)
    if body == nil {
        return
    }

    var prettyJSON bytes.Buffer
    error := json.Indent(&prettyJSON, body, "", "\t")
    if error != nil {
        log.Println("JSON parse error: ", error)
        App.BadRequest(w)
        return
    }

    log.Println("CSP Violation:", string(prettyJSON.Bytes()))
}
Robyoder
quelle
48

Für eine bessere Speichernutzung ist dies vermutlich besser:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}
mh-cbon
quelle
Wurde SetIndentkürzlich hinzugefügt? Es ist den meisten im Wesentlichen unbekannt.
Chappjc
1
@chappjc SetIndent(ursprünglich benannt Indent) wurde anscheinend im März 2016 hinzugefügt und in Go 1.7 veröffentlicht, ungefähr 3 Jahre nachdem diese Frage ursprünglich gestellt wurde: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu
20

Ich war frustriert über das Fehlen einer schnellen und qualitativ hochwertigen Möglichkeit, JSON für eine kolorierte Zeichenfolge in Go zu marshallen, und schrieb meinen eigenen Marshaller namens ColorJSON .

Mit ihm können Sie auf einfache Weise eine Ausgabe wie diese mit sehr wenig Code erstellen:

ColorJSON-Beispielausgabe

package main

import (
    "fmt"
    "encoding/json"

    "github.com/TylerBrock/colorjson"
)

func main() {
    str := `{
      "str": "foo",
      "num": 100,
      "bool": false,
      "null": null,
      "array": ["foo", "bar", "baz"],
      "obj": { "a": 1, "b": 2 }
    }`

    var obj map[string]interface{}
    json.Unmarshal([]byte(str), &obj)

    // Make a custom formatter with indent set
    f := colorjson.NewFormatter()
    f.Indent = 4

    // Marshall the Colorized JSON
    s, _ := f.Marshal(obj)
    fmt.Println(string(s))
}

Ich schreibe gerade die Dokumentation dafür, aber ich war begeistert, meine Lösung zu teilen.

Tyler Brock
quelle
17

Bearbeiten Rückblickend ist dies nicht idiomatisch Go. Kleine Hilfsfunktionen wie diese erhöhen die Komplexität zusätzlich. Im Allgemeinen zieht die Go-Philosophie es vor, die 3 einfachen Zeilen gegenüber einer kniffligen Zeile einzuschließen.


Wie @robyoder erwähnt, json.Indentist der richtige Weg. Ich dachte, ich würde diese kleine prettyprintFunktion hinzufügen :

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
)

//dont do this, see above edit
func prettyprint(b []byte) ([]byte, error) {
    var out bytes.Buffer
    err := json.Indent(&out, b, "", "  ")
    return out.Bytes(), err
}

func main() {
    b := []byte(`{"hello": "123"}`)
    b, _ = prettyprint(b)
    fmt.Printf("%s", b)
}

https://go-sandbox.com/#/R4LWpkkHIN oder http://play.golang.org/p/R4LWpkkHIN

jpillora
quelle
7

Folgendes benutze ich. Wenn der JSON nicht schön gedruckt werden kann, wird nur die ursprüngliche Zeichenfolge zurückgegeben. Nützlich zum Drucken von HTTP-Antworten, die JSON enthalten sollten .

import (
    "encoding/json"
    "bytes"
)

func jsonPrettyPrint(in string) string {
    var out bytes.Buffer
    err := json.Indent(&out, []byte(in), "", "\t")
    if err != nil {
        return in
    }
    return out.String()
}
Timmmm
quelle
6

Hier ist meine Lösung :

import (
    "bytes"
    "encoding/json"
)

const (
    empty = ""
    tab   = "\t"
)

func PrettyJson(data interface{}) (string, error) {
    buffer := new(bytes.Buffer)
    encoder := json.NewEncoder(buffer)
    encoder.SetIndent(empty, tab)

    err := encoder.Encode(data)
    if err != nil {
       return empty, err
    }
    return buffer.String(), nil
}
Raed Shomali
quelle
2

Ein einfacher hübscher Drucker von der Stange in Go. Man kann es zu einer Binärdatei kompilieren durch:

go build -o jsonformat jsonformat.go

Es liest von der Standardeingabe, schreibt in die Standardausgabe und ermöglicht das Festlegen von Einrückungen:

package main

import (
    "bytes"
    "encoding/json"
    "flag"
    "fmt"
    "io/ioutil"
    "os"
)

func main() {
    indent := flag.String("indent", "  ", "indentation string/character for formatter")
    flag.Parse()
    src, err := ioutil.ReadAll(os.Stdin)
    if err != nil {
        fmt.Fprintf(os.Stderr, "problem reading: %s", err)
        os.Exit(1)
    }

    dst := &bytes.Buffer{}
    if err := json.Indent(dst, src, "", *indent); err != nil {
        fmt.Fprintf(os.Stderr, "problem formatting: %s", err)
        os.Exit(1)
    }
    if _, err = dst.WriteTo(os.Stdout); err != nil {
        fmt.Fprintf(os.Stderr, "problem writing: %s", err)
        os.Exit(1)
    }
}

Es ermöglicht das Ausführen von Bash-Befehlen wie:

cat myfile | jsonformat | grep "key"
Paweł Szczur
quelle
2
package cube

import (
    "encoding/json"
    "fmt"
    "github.com/magiconair/properties/assert"
    "k8s.io/api/rbac/v1beta1"
    v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    "testing"
)

func TestRole(t *testing.T)  {
    clusterRoleBind := &v1beta1.ClusterRoleBinding{
        ObjectMeta: v1.ObjectMeta{
            Name: "serviceaccounts-cluster-admin",
        },
        RoleRef: v1beta1.RoleRef{
            APIGroup: "rbac.authorization.k8s.io",
            Kind:     "ClusterRole",
            Name:     "cluster-admin",
        },
        Subjects: []v1beta1.Subject{{
            Kind:     "Group",
            APIGroup: "rbac.authorization.k8s.io",
            Name:     "system:serviceaccounts",
        },
        },
    }
    b, err := json.MarshalIndent(clusterRoleBind, "", "  ")
    assert.Equal(t, nil, err)
    fmt.Println(string(b))
}

Wie es aussieht

Clare Chu
quelle
1

Ich bin ein bisschen neu, aber das habe ich bisher gesammelt:

package srf

import (
    "bytes"
    "encoding/json"
    "os"
)

func WriteDataToFileAsJSON(data interface{}, filedir string) (int, error) {
    //write data as buffer to json encoder
    buffer := new(bytes.Buffer)
    encoder := json.NewEncoder(buffer)
    encoder.SetIndent("", "\t")

    err := encoder.Encode(data)
    if err != nil {
        return 0, err
    }
    file, err := os.OpenFile(filedir, os.O_RDWR|os.O_CREATE, 0755)
    if err != nil {
        return 0, err
    }
    n, err := file.Write(buffer.Bytes())
    if err != nil {
        return 0, err
    }
    return n, nil
}

Dies ist die Ausführung der Funktion und nur Standard

b, _ := json.MarshalIndent(SomeType, "", "\t")

Code:

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"

    minerals "./minerals"
    srf "./srf"
)

func main() {

    //array of Test struct
    var SomeType [10]minerals.Test

    //Create 10 units of some random data to write
    for a := 0; a < 10; a++ {
        SomeType[a] = minerals.Test{
            Name:   "Rand",
            Id:     123,
            A:      "desc",
            Num:    999,
            Link:   "somelink",
            People: []string{"John Doe", "Aby Daby"},
        }
    }

    //writes aditional data to existing file, or creates a new file
    n, err := srf.WriteDataToFileAsJSON(SomeType, "test2.json")
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("srf printed ", n, " bytes to ", "test2.json")

    //overrides previous file
    b, _ := json.MarshalIndent(SomeType, "", "\t")
    ioutil.WriteFile("test.json", b, 0644)

}
accnameowl
quelle
0
//You can do it with json.MarshalIndent(data, "", "  ")

package main

import(
  "fmt"
  "encoding/json" //Import package
)

//Create struct
type Users struct {
    ID   int
    NAME string
}

//Asign struct
var user []Users
func main() {
 //Append data to variable user
 user = append(user, Users{1, "Saturn Rings"})
 //Use json package the blank spaces are for the indent
 data, _ := json.MarshalIndent(user, "", "  ")
 //Print json formatted
 fmt.Println(string(data))
}
Illud
quelle