Come posso stampare graziosamente JSON usando Go?


191

Qualcuno sa un modo semplice per stampare graziosamente l'output JSON in Go?

Il pacchetto http://golang.org/pkg/encoding/json/ non sembra includere funzionalità per questo (EDIT: sì, vedi risposta accettata) e un veloce google non rivela nulla di ovvio.

Gli usi che sto cercando sono sia il risultato di stampa piuttosto che la json.Marshalformattazione di una stringa piena di JSON da qualsiasi luogo, quindi è più facile da leggere per scopi di debug.


Avvertenza: nei miei esperimenti, nei dizionari JSON gli indici di stringhe devono essere racchiusi tra parentesi. Quindi, {name: "value"}non andrà bene, nonostante la maggior parte dell'interprete Javascript lo usi . Funzionerà solo {"name": "value"} con le funzioni della libreria Go JSON.
Peter - Ripristina Monica il

2
@peterh Penso che tu stia confondendo la sintassi letterale JavaScript con JSON corretta. La specifica JSON ( json.org ) indica chiaramente che sono consentiti solo valori letterali di stringa (il che significa che necessita di virgolette), mentre la sintassi dell'oggetto linguaggio JS non ha questa limitazione. La libreria Go sta seguendo le specifiche.
Brad Peabody,

Risposte:


298

Per bella stampa, presumo che tu intenda rientrato, in questo modo

{
    "data": 1234
}

piuttosto che

{"data":1234}

Il modo più semplice per farlo è con MarshalIndent, che ti permetterà di specificare come desideri rientrare tramite l' indentargomento. Pertanto, json.MarshalIndent(data, "", " ")verrà stampato piuttosto utilizzando quattro spazi per il rientro.


17
Sì, sembra proprio il fatto: è già integrato, l'unica cosa che rimane è includere la parola chiave "pretty-print" nel documento pkg in modo che il prossimo ragazzo che la trova lo trovi. (Lascerà una nota di feedback per i manutentori del documento.) Tks!
Brad Peabody,

39
json.MarshalIndent(data, "", "\t")se vuoi le schede.
Kyle Brandt,

82
json.MarshalIndent(data, "", "🐱")se vuoi i gatti. mi dispiace
briiC

46
json.MarshalIndent(data, "", "\t🐱")se vuoi ... gatti soriano ... scusa
Davos,

78

La risposta accettata è ottima se hai un oggetto che vuoi trasformare in JSON. La domanda menziona anche la stampa carina di qualsiasi stringa JSON, ed è quello che stavo cercando di fare. Volevo solo registrare un po 'di JSON da una richiesta POST (in particolare un rapporto di violazione CSP ).

Per usarlo MarshalIndent, dovresti farlo Unmarshalin un oggetto. Se ne hai bisogno, provaci, ma io no. Se hai solo bisogno di stampare graziosamente un array di byte, plain Indentè tuo amico.

Ecco cosa ho finito con:

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

48

Per un migliore utilizzo della memoria, suppongo che sia meglio:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}

Sei SetIndentstato aggiunto di recente? È essenzialmente sconosciuto ai più.
Chappjc,

1
A quanto pare, @chappjc SetIndent(originariamente chiamato Indent) è stato aggiunto a marzo 2016 e rilasciato in Go 1.7, circa 3 anni dopo la domanda iniziale: github.com/golang/go/commit/… github.com/golang/go/commit/ ...
aoeu,

19

Ero frustrato dalla mancanza di un modo rapido e di alta qualità per eseguire il marshalling JSON su una stringa colorata in Go, così ho scritto il mio Marshaller chiamato ColorJSON .

Con esso, puoi facilmente produrre output come questo usando pochissimo codice:

Output di esempio ColorJSON

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

Sto scrivendo la documentazione per ora, ma ero entusiasta di condividere la mia soluzione.


17

Modifica Guardando indietro, questo non è idiomatico Vai. Le funzioni di supporto di piccole dimensioni come questa aggiungono un ulteriore livello di complessità. In generale, la filosofia Go preferisce includere le 3 linee semplici su 1 linea delicata.


Come menzionato @robyoder, json.Indentè la strada da percorrere. Ho pensato di aggiungere questa piccola prettyprintfunzione:

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 o http://play.golang.org/p/R4LWpkkHIN


7

Ecco cosa uso. Se non riesce a stampare abbastanza JSON, restituisce solo la stringa originale. Utile per la stampa di risposte HTTP che dovrebbero contenere JSON.

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

6

Ecco la mia soluzione :

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
}

2

Una semplice e graziosa stampante pronta all'uso in Go. Si può compilarlo in un file binario attraverso:

go build -o jsonformat jsonformat.go

Legge da input standard, scrive in output standard e consente di impostare il rientro:

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

Permette di eseguire comandi bash come:

cat myfile | jsonformat | grep "key"

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

Come sembra


1

sono un po 'nuovo per andare, ma questo è quello che ho raccolto finora:

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
}

Questa è l'esecuzione della funzione, e solo standard

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

Codice:

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)

}

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))
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.