337

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

I'd like to pretty-print the result of json.Marshal, as well as formatting an existing string of JSON so it's easier to read.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
Brad Peabody
  • 10,917
  • 9
  • 44
  • 63
  • 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 Aug 16 '17 at 19:18
  • 4
    @peterh I think you're confusing JavaScript literal syntax with JSON proper. The JSON spec (http://www.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. – Brad Peabody Aug 16 '17 at 19:23

15 Answers15

497

MarshalIndent will allow you to output your JSON with indentation and spacing. For example:

{
    "data": 1234
}

The indent argument specifies the series of characters to indent with. Thus, json.MarshalIndent(data, "", " ") will pretty-print using four spaces for indentation.

Hymns For Disco
  • 7,530
  • 2
  • 17
  • 33
Alexander Bauer
  • 10,663
  • 8
  • 29
  • 40
  • 32
    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! – Brad Peabody Sep 26 '13 at 21:52
  • 13
    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))` – ZAR Jun 23 '21 at 14:37
187

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()))
}
robyoder
  • 2,366
  • 1
  • 12
  • 13
  • 12
    Thanks! This was very helpful. Just one minor comment that instead of `string(prettyJSON.Bytes())` you can do `prettyJSON.String()` – Cassio Jul 29 '21 at 14:54
  • 1
    Cool, didn't know this exists! Perfect for low-impact debug logging. – Rob Jan 27 '22 at 09:16
  • 4
    @Cassio Good point. Actually, we can just do `&prettyJSON`, because `log` or `fmt` executes `prettyJSON.String()` internally. – starriet Apr 08 '22 at 13:09
63

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)
}
codehakase
  • 20
  • 5
  • Did `SetIndent` get added recently? It's essentially unknown to most. – chappjc Mar 25 '17 at 17:37
  • 4
    @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: https://github.com/golang/go/commit/098b62644f9388a8afba90d3e74ea7d7497def4c https://github.com/golang/go/commit/34b17d4dc5726eebde437f2c1b680d039cc3e7c0 – aoeu Feb 28 '19 at 23:42
  • Any memory comparisons between this and the usage of `json.MarshalIndent(..)` ? – Chen A. May 14 '21 at 17:30
  • 1
    @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. –  May 14 '21 at 18:49
36

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:

ColorJSON sample output

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.

Oliver
  • 11,857
  • 2
  • 36
  • 42
Tyler Brock
  • 29,626
  • 15
  • 79
  • 79
27

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

jpillora
  • 5,194
  • 2
  • 44
  • 56
16

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()
}
peterh
  • 11,875
  • 18
  • 85
  • 108
Timmmm
  • 88,195
  • 71
  • 364
  • 509
7
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))
}

How it looks like

SamuelTJackson
  • 1,357
  • 3
  • 19
  • 40
Clare Chu
  • 649
  • 6
  • 5
6

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
}
Nathan Tuggy
  • 2,237
  • 27
  • 30
  • 38
Raed Shomali
  • 1,385
  • 15
  • 7
5
//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
  • 319
  • 1
  • 4
  • 12
4

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

Go Playground

Carson
  • 6,105
  • 2
  • 37
  • 45
3

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

Ajmal Moochingal
  • 177
  • 1
  • 10
2

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"
Paweł Szczur
  • 5,484
  • 3
  • 29
  • 32
1

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)

}
accnameowl
  • 21
  • 2
1

Here is a short json-string to json-string conversion example with indent prettyprint without any struct-objects.

    str := `{"a":{"key":"val"}}`
    data := []byte(str)
    empty := []byte{}
    buf := bytes.NewBuffer(empty)
    json.Indent(buf, data, "", "    ")
    readBuf, _ := ioutil.ReadAll(buf)
    fmt.Println((string)(readBuf))
zeg
  • 432
  • 2
  • 9
0

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