ChatGPT解决这个技术问题 Extra ChatGPT

How can I pretty-print JSON using Go?

Does anyone know of a simple way to pretty-print JSON output in Go?

The stock http://golang.org/pkg/encoding/json/ package does not seem to include functionality for this (EDIT: it does, see accepted answer) and a quick google doesn't turn up anything obvious.

Uses I'm looking for are both pretty-printing the result of json.Marshal and just formatting a string full of JSON from wherever, so it's easier to read for debug purposes.

Warning: on my experiments, in JSON dictionaries the strings indices must be enclosed in parentheses. So, {name: "value"} won't be okay, despite that most Javascript interpreter uses it. Only {"name": "value"} will work with the Go JSON library functions.
@peterh I think you're confusing JavaScript literal syntax with JSON proper. The JSON spec (json.org) clearly indicates that only string literals are allowed (meaning it needs quotes), while JS language object syntax does not have that restriction. The Go library is following the spec.

A
Alexander Bauer

By pretty-print, I assume you mean indented, like so

{
    "data": 1234
}

rather than

{"data":1234}

The easiest way to do this is with MarshalIndent, which will let you specify how you would like it indented via the indent argument. Thus, json.MarshalIndent(data, "", " ") will pretty-print using four spaces for indentation.


Yeah, that looks like just the thing - it's already built-in, only left is to include the keyword "pretty-print" in the pkg doc so the next guy searching finds it. (Will leave a feedback note for the doc maintainers.) Tks!
json.MarshalIndent(data, "", "\t") if you want tabs.
json.MarshalIndent(data, "", "\t🐱") if you want ... tabby cats... sorry
json.MarshalIndent(data, "", " 🐱") if you want .... spaced cats... sorry
In case you're trying to print this json to console: MarshalIndent returns a ([]byte, error). Just pass the []byte to string() and print, e.g. j, _ := json.MarshalIndent(data, "", "🐱"); fmt.Println(string(j))
r
robyoder

The accepted answer is great if you have an object you want to turn into JSON. The question also mentions pretty-printing just any JSON string, and that's what I was trying to do. I just wanted to pretty-log some JSON from a POST request (specifically a CSP violation report).

To use MarshalIndent, you would have to Unmarshal that into an object. If you need that, go for it, but I didn't. If you just need to pretty-print a byte array, plain Indent is your friend.

Here's what I ended up with:

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

Thanks! This was very helpful. Just one minor comment that instead of string(prettyJSON.Bytes()) you can do prettyJSON.String()
Cool, didn't know this exists! Perfect for low-impact debug logging.
@Cassio Good point. Actually, we can just do &prettyJSON, because log or fmt executes prettyJSON.String() internally.
c
codehakase

For better memory usage, I guess this is better:

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

Did SetIndent get added recently? It's essentially unknown to most.
@chappjc SetIndent (originally named Indent) was apparently added March 2016 and released in Go 1.7, which was about 3 years after this question was originally asked: github.com/golang/go/commit/… github.com/golang/go/commit/…
Any memory comparisons between this and the usage of json.MarshalIndent(..) ?
@ChenA. this does not really appear needed. Both implementation are pretty clear about their pros and cons. For any object, which has size in memory > the length of the buffer used to stream marshal it, a stream encoder will consume less memory than a non stream-encoder. That last encoder needs to hold both representaton of the same data in memory, the original and its encoded version.
O
Oliver

I was frustrated by the lack of a fast, high quality way to marshal JSON to a colorized string in Go so I wrote my own Marshaller called ColorJSON.

With it, you can easily produce output like this using very little code:

https://i.stack.imgur.com/dLJ3j.png

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

I'm writing the documentation for it now but I was excited to share my solution.


Thank you very much! Very cool package, used it for my commercial needs!
j
jpillora

Edit Looking back, this is non-idiomatic Go. Small helper functions like this add an extra step of complexity. In general, the Go philosophy prefers to include the 3 simple lines over 1 tricky line.

As @robyoder mentioned, json.Indent is the way to go. Thought I'd add this small prettyprint function:

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


p
peterh

Here's what I use. If it fails to pretty print the JSON it just returns the original string. Useful for printing HTTP responses that should contain 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()
}

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

https://i.stack.imgur.com/JWPwg.png


N
Nathan Tuggy

Here is my solution:

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
}

I
Illud
//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))
}

C
Carson

Another example with http.ResponseWriter.

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

func main() {
    var w http.ResponseWriter

    type About struct {
        ProgName string
        Version string
    }
    goObj := About{ProgName: "demo", Version: "0.0.0"}
    beautifulJsonByte, err := json.MarshalIndent(goObj, "", "  ")
    if err != nil {
        panic(err)
    }
    _, _ = w.Write(beautifulJsonByte)
}

output

{
  "ProgName": "demo",
  "Version": "0.0.0"
}

https://img.shields.io/badge/Go-Playground-5593c7.svg?labelColor=41c3f3&style=for-the-badge


P
Paweł Szczur

A simple off the shelf pretty printer in Go. One can compile it to a binary through:

go build -o jsonformat jsonformat.go

It reads from standard input, writes to standard output and allow to set indentation:

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

It allows to run a bash commands like:

cat myfile | jsonformat | grep "key"

A
Ajmal Moochingal

If you want to create a commandline utility to pretty print JSON


package main

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


func main(){

    var out bytes.Buffer

    reader := bufio.NewReader(os.Stdin)
    text, _ := reader.ReadString('\n')

    err := json.Indent(&out, []byte(text), "", "  ")
    if err != nil {
      fmt.Println(err)
    }

    fmt.Println(string(out.Bytes()))
}

echo "{\"boo\":\"moo\"}" | go run main.go 

will produce the following output :

{
  "boo": "moo"
}

feel free to build a binary

go build main.go

and drop it in /usr/local/bin


a
accnameowl

i am sort of new to go, but this is what i gathered up so far:

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
}

This is the execution of the function, and just 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)

}

B
Bilal Saidumarov

Use json.MarshalIndent with string

This easyPrint function accepts argument data (any type of data) to print it into the intended (pretty) JSON format.

import (
  "encoding/json"
  "log"
)

func easyPrint(data interface{}) {
  manifestJson, _ := json.MarshalIndent(data, "", "  ")

  log.Println(string(manifestJson))
}

With name argument.

TODO: make argument name optional.

func easyPrint(data interface{}, name string) {
  manifestJson, _ := json.MarshalIndent(data, "", "  ")

  log.Println(name + " ->", string(manifestJson))
}