Como posso imprimir JSON usando Go?

191

Alguém conhece uma maneira simples de imprimir bastante a saída JSON no Go?

O pacote http://golang.org/pkg/encoding/json/ parece não incluir funcionalidade para isso (EDIT: faz, veja resposta aceita) e um rápido google não mostra nada óbvio.

Os usos que estou procurando estão imprimindo bastante o resultado json.Marshale apenas formatando uma string cheia de JSON de qualquer lugar, para que seja mais fácil ler para fins de depuração.

Brad Peabody
fonte
Aviso: nas minhas experiências, nos dicionários JSON, os índices de strings devem estar entre parênteses. Portanto, {name: "value"}não ficará bem, apesar de a maioria dos intérpretes Javascript usá-lo . Apenas {"name": "value"} funcionará com as funções da biblioteca Go JSON.
peterh - Restabelece Monica
2
@ Peterh Acho que você está confundindo a sintaxe literal do JavaScript com o JSON. A especificação JSON ( json.org ) indica claramente que apenas literais de seqüência de caracteres são permitidos (o que significa que precisa de aspas), enquanto a sintaxe do objeto de linguagem JS não possui essa restrição. A biblioteca Go está seguindo as especificações.
Brad Peabody

Respostas:

297

Por impressão bonita, suponho que você quer dizer recuado, assim

{
    "data": 1234
}

ao invés de

{"data":1234}

A maneira mais fácil de fazer isso é usando MarshalIndent, o que permitirá que você especifique como gostaria que fosse recuado por meio do indentargumento. Assim, json.MarshalIndent(data, "", " ")imprimirá bastante usando quatro espaços para indentação.

Alexander Bauer
fonte
17
Sim, isso parece exatamente a coisa - já está embutido, resta apenas incluir a palavra-chave "pretty-print" no documento do pkg para que o próximo pesquisador a encontre. (Deixará uma nota de feedback para os mantenedores de documentos.) Tks!
Brad Peabody
39
json.MarshalIndent(data, "", "\t")se você quiser abas.
Kyle Brandt
82
json.MarshalIndent(data, "", "🐱")se você quer gatos. desculpe
briiC
46
json.MarshalIndent(data, "", "\t🐱")se você quiser ... gatos tigrados ... desculpe #
294 Davos
78

A resposta aceita é ótima se você tiver um objeto que deseja transformar em JSON. A pergunta também menciona a impressão bonita de qualquer string JSON, e era isso que eu estava tentando fazer. Eu só queria registrar bastante o JSON de uma solicitação POST (especificamente um relatório de violação do CSP ).

Para usar MarshalIndent, você precisaria Unmarshaldisso em um objeto. Se você precisar, vá em frente, mas eu não. Se você só precisa imprimir uma matriz de bytes, o Indentseu amigo é simples .

Aqui está o que eu acabei com:

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

Para melhor uso da memória, acho que é melhor:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}
mh-cbon
fonte
Você SetIndentfoi adicionado recentemente? É essencialmente desconhecido para a maioria.
Chappjc
1
Aparentemente, o @chappjc SetIndent(originalmente chamado Indent) foi adicionado em março de 2016 e lançado no Go 1.7, cerca de 3 anos após a pergunta inicial: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu 28/02/19
19

Fiquei frustrado com a falta de uma maneira rápida e de alta qualidade de ordenar JSON para uma sequência colorida no Go, então escrevi meu próprio Marshaller chamado ColorJSON .

Com ele, você pode produzir facilmente uma saída como essa usando muito pouco código:

Saída de amostra 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))
}

Estou escrevendo a documentação para isso agora, mas fiquei empolgado em compartilhar minha solução.

Tyler Brock
fonte
17

Editar Olhando para trás, isso é Go não-idiomático. Funções auxiliares pequenas como essa adicionam uma etapa extra de complexidade. Em geral, a filosofia Go prefere incluir as 3 linhas simples em vez de 1 linha complicada.


Como o @robyoder mencionou, json.Indenté o caminho a percorrer. Pensei em adicionar esta pequena prettyprintfunção:

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

jpillora
fonte
7

Aqui está o que eu uso. Se ele não conseguir imprimir o JSON, apenas retornará a string original. Útil para imprimir respostas HTTP que devem conter 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
fonte
6

Aqui está a minha solução :

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

Uma simples impressora pronta para uso no Go. Pode-se compilá-lo em um binário através de:

go build -o jsonformat jsonformat.go

Ele lê da entrada padrão, grava na saída padrão e permite definir o recuo:

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

Permite executar comandos bash como:

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

Como isso parece

Clare Chu
fonte
1

Eu sou uma espécie de novato, mas é isso que eu reuni até agora:

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
}

Esta é a execução da função, e apenas o padrão

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

Código:

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