Jak mogę ładnie wydrukować JSON za pomocą Go?

191

Czy ktoś wie o prostym sposobie drukowania wydruków JSON w Go?

Standardowy pakiet http://golang.org/pkg/encoding/json/ nie wydaje się zawierać w tym celu funkcji (EDYCJA: tak, zobacz zaakceptowaną odpowiedź), a szybkie google nie pokazuje niczego oczywistego.

Zastosowania, których szukam, zarówno ładnie drukują wynik, jak json.Marshali po prostu formatują ciąg pełen JSON z dowolnego miejsca, więc łatwiej jest czytać do celów debugowania.

Brad Peabody
źródło
Ostrzeżenie: w moich eksperymentach w słownikach JSON indeksy ciągów znaków muszą być ujęte w nawiasy. Więc {name: "value"}nie będzie w porządku, mimo że większość interpreterów JavaScript go używa . Działa tylko {"name": "value"} z funkcjami biblioteki Go JSON.
peterh - Przywróć Monikę
2
@peterh Myślę, że mylisz dosłowną składnię JavaScript z właściwym JSON. Specyfikacja JSON ( json.org ) wyraźnie wskazuje, że dozwolone są tylko literały łańcuchowe (co oznacza, że ​​wymaga cudzysłowów), podczas gdy składnia obiektu języka JS nie ma tego ograniczenia. Biblioteka Go jest zgodna ze specyfikacją.
Brad Peabody,

Odpowiedzi:

296

Przez ładny druk zakładam, że masz na myśli wcięcia

{
    "data": 1234
}

zamiast

{"data":1234}

Najłatwiej to zrobić za pomocą MarshalIndent, który pozwoli ci określić, w jaki sposób chcesz wciąć za pomocą indentargumentu. Tak więc json.MarshalIndent(data, "", " ")będzie ładnie drukować przy użyciu czterech spacji do wcięcia.

Alexander Bauer
źródło
17
Tak, to wygląda tylko na to - jest już wbudowane, pozostało tylko umieścić słowo kluczowe „ładny druk” w dokumencie pkg, aby następny przeszukiwacz go znalazł. (Zostawi notatkę zwrotną dla opiekunów dokumentów.) Tks!
Brad Peabody,
39
json.MarshalIndent(data, "", "\t")jeśli chcesz zakładki.
Kyle Brandt,
80
json.MarshalIndent(data, "", "🐱")jeśli chcesz koty. przepraszam
briiC
45
json.MarshalIndent(data, "", "\t🐱")jeśli chcesz ... pręgowane koty ... przepraszam
Davos
78

Przyjęta odpowiedź jest świetna, jeśli masz obiekt, który chcesz zmienić w JSON. Pytanie wspomina również o ładnym drukowaniu dowolnego łańcucha JSON i właśnie to próbowałem zrobić. Chciałem tylko całkiem zalogować JSON z żądania POST (konkretnie raport o naruszeniu CSP ).

Aby użyć MarshalIndent, musisz to zrobić Unmarshalw obiekcie. Jeśli potrzebujesz tego, idź, ale nie zrobiłem tego. Jeśli chcesz po prostu wydrukować tablicę bajtów, zwykły Indentjest twoim przyjacielem.

Oto, z czym skończyłem:

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

Myślę, że dla lepszego wykorzystania pamięci jest to lepsze:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}
mh-cbon
źródło
Czy SetIndentzostał ostatnio dodany? Zasadniczo jest to nieznane większości.
chappjc,
1
@chappjc SetIndent(pierwotnie nazwany Indent) najwyraźniej został dodany w marcu 2016 r. i wydany w wersji Go 1.7, co było około 3 lata po pierwotnym zadaniu tego pytania: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu
20

Byłem sfrustrowany brakiem szybkiego, wysokiej jakości sposobu na doprowadzenie JSON do pokolorowanego sznurka w Go, więc napisałem własnego Marshallera o nazwie ColorJSON .

Dzięki niemu możesz łatwo tworzyć takie dane wyjściowe, używając bardzo małego kodu:

Wyjście próbki 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))
}

Piszę teraz dokumentację na ten temat, ale byłem podekscytowany, mogąc podzielić się moim rozwiązaniem.

Tyler Brock
źródło
17

Edytuj Patrząc wstecz, nie jest to idiomatyczne Go. Takie małe funkcje pomocnicze zwiększają złożoność. Ogólnie filozofia Go woli obejmować 3 proste linie zamiast 1 trudnej linii.


Jak wspomniano @robyoder, json.Indentjest to najlepsza droga. Pomyślałem, że dodam tę małą prettyprintfunkcję:

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

jpillora
źródło
7

Oto czego używam. Jeśli nie uda się wydrukować JSON, po prostu zwraca oryginalny ciąg. Przydatne do drukowania odpowiedzi HTTP, które powinny zawierać 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()
}
Timmmm
źródło
6

Oto moje rozwiązanie :

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

Prosta, ładna drukarka z półki w Go. Można skompilować go do pliku binarnego poprzez:

go build -o jsonformat jsonformat.go

Odczytuje ze standardowego wejścia, zapisuje na standardowe wyjście i pozwala ustawić wcięcie:

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

Pozwala na uruchamianie poleceń bash, takich jak:

cat myfile | jsonformat | grep "key"
Paweł Szczur
źródło
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))
}

Jak to wygląda

Clare Chu
źródło
1

jestem trochę nowy, ale jak dotąd zebrałem:

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
}

To jest wykonanie funkcji i tylko standardowe

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

Kod:

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