Bagaimana cara mencetak JSON dengan cantik menggunakan Go?

191

Adakah yang tahu cara sederhana untuk mencetak keluaran JSON di Go?

Paket stok http://golang.org/pkg/encoding/json/ tampaknya tidak menyertakan fungsionalitas untuk ini (EDIT: ya, lihat jawaban yang diterima) dan google cepat tidak muncul dengan jelas.

Penggunaan yang saya cari sama-sama mencetak hasil json.Marshaldan hanya memformat string penuh JSON dari mana saja, jadi lebih mudah dibaca untuk keperluan debug.

Brad Peabody
sumber
Peringatan: pada percobaan saya, dalam kamus JSON, indeks string harus dilampirkan dalam tanda kurung. Jadi, {name: "value"}tidak akan apa-apa, meskipun sebagian besar penerjemah Javascript menggunakannya . Hanya {"name": "value"} akan berfungsi dengan fungsi pustaka Go JSON.
peterh
2
@peterh Saya pikir Anda membingungkan sintaks literal JavaScript dengan JSON yang tepat. Spesifikasi JSON ( json.org ) dengan jelas menunjukkan bahwa hanya string literal yang diizinkan (artinya perlu kutipan), sedangkan sintaks objek bahasa JS tidak memiliki batasan itu. Pustaka Go mengikuti spesifikasi.
Brad Peabody

Jawaban:

298

Dengan cukup-cetak, saya menganggap Anda maksud indentasi, seperti itu

{
    "data": 1234
}

daripada

{"data":1234}

Cara termudah untuk melakukan ini adalah dengan MarshalIndent, yang akan memungkinkan Anda menentukan bagaimana Anda ingin indentasi melalui indentargumen. Dengan demikian, json.MarshalIndent(data, "", " ")akan cukup mencetak menggunakan empat spasi untuk lekukan.

Alexander Bauer
sumber
17
Ya, sepertinya itu masalahnya - itu sudah built-in, hanya tersisa untuk memasukkan kata kunci "cukup-cetak" dalam dokumen pkg sehingga orang berikutnya yang mencari menemukannya. (Akan meninggalkan catatan umpan balik untuk pengelola dokumen.) Tks!
Brad Peabody
39
json.MarshalIndent(data, "", "\t")jika Anda ingin tab.
Kyle Brandt
82
json.MarshalIndent(data, "", "🐱")jika Anda ingin kucing. maaf
briiC
46
json.MarshalIndent(data, "", "\t🐱")jika Anda ingin ... kucing kucing ... maaf
Davos
78

Jawaban yang diterima sangat bagus jika Anda memiliki objek yang ingin Anda ubah menjadi JSON. Pertanyaannya juga menyebutkan cukup mencetak sembarang string JSON, dan itulah yang saya coba lakukan. Saya hanya ingin mencatat beberapa JSON dari permintaan POST (khususnya laporan pelanggaran CSP ).

Untuk menggunakannya MarshalIndent, Anda harus Unmarshalmengubahnya menjadi objek. Jika Anda membutuhkannya, lakukan saja, tetapi saya tidak melakukannya. Jika Anda hanya perlu mencetak array byte yang cantik, plain Indentadalah teman Anda.

Inilah yang akhirnya saya dapatkan:

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
sumber
48

Untuk penggunaan memori yang lebih baik, saya kira ini lebih baik:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}
mh-cbon
sumber
Apakah SetIndentditambahkan baru-baru ini? Ini pada dasarnya tidak diketahui oleh kebanyakan orang.
chappjc
1
@chappjc SetIndent(awalnya bernama Indent) tampaknya ditambahkan Maret 2016 dan dirilis di Go 1.7, yang sekitar 3 tahun setelah pertanyaan ini awalnya diajukan: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu
19

Saya merasa frustrasi dengan kurangnya cara yang cepat dan berkualitas tinggi untuk menyusun JSON ke string berwarna di Go sehingga saya menulis Marshaller saya sendiri yang disebut ColorJSON .

Dengannya, Anda dapat dengan mudah menghasilkan output seperti ini menggunakan kode yang sangat sedikit:

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

Saya sedang menulis dokumentasi untuk itu sekarang tetapi saya bersemangat untuk membagikan solusi saya.

Tyler Brock
sumber
17

Sunting Melihat ke belakang, ini adalah Go non-idiomatik. Fungsi pembantu kecil seperti ini menambah langkah kompleksitas tambahan. Secara umum, filosofi Go lebih suka memasukkan 3 baris sederhana di atas 1 garis rumit.


Seperti @robyoder sebutkan, json.Indentadalah caranya. Kupikir aku akan menambahkan prettyprintfungsi kecil ini :

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

jpillora
sumber
7

Inilah yang saya gunakan. Jika gagal mencetak JSON, itu hanya mengembalikan string asli. Berguna untuk mencetak respons HTTP yang seharusnya berisi 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
sumber
6

Ini solusinya :

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
sumber
2

Printer sederhana sederhana dari rak di Go. Satu dapat mengkompilasinya ke biner melalui:

go build -o jsonformat jsonformat.go

Bunyinya dari input standar, menulis ke output standar dan memungkinkan untuk mengatur indentasi:

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

Ini memungkinkan untuk menjalankan perintah bash seperti:

cat myfile | jsonformat | grep "key"
Paweł Szczur
sumber
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))
}

Seperti apa bentuknya

Clare Chu
sumber
1

saya agak baru untuk pergi, tapi ini yang saya kumpulkan sejauh ini:

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
}

Ini adalah eksekusi fungsi, dan hanya standar

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

Kode:

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