¿Cómo puedo imprimir JSON con Go?

191

¿Alguien sabe de una manera simple de imprimir bonita salida JSON en Go?

El paquete http://golang.org/pkg/encoding/json/ no parece incluir funcionalidades para esto (EDITAR: sí, ver respuesta aceptada) y un google rápido no muestra nada obvio.

Los usos que estoy buscando son una impresión bonita del resultado json.Marshaly solo formatean una cadena llena de JSON desde cualquier lugar, por lo que es más fácil de leer con fines de depuración.

Brad Peabody
fuente
Advertencia: en mis experimentos, en los diccionarios JSON, los índices de cadenas deben estar entre paréntesis. Entonces, {name: "value"}no estará bien, a pesar de que la mayoría de los intérpretes de Javascript lo usan . Solo {"name": "value"} funcionará con las funciones de la biblioteca Go JSON.
peterh - Restablece a Mónica el
2
@peterh Creo que estás confundiendo la sintaxis literal de JavaScript con JSON propiamente dicha. La especificación JSON ( json.org ) indica claramente que solo se permiten literales de cadena (lo que significa que necesita comillas), mientras que la sintaxis del objeto de lenguaje JS no tiene esa restricción. La biblioteca Go sigue las especificaciones.
Brad Peabody

Respuestas:

298

Por letra bonita, supongo que te refieres a sangrado, así

{
    "data": 1234
}

más bien que

{"data":1234}

La forma más fácil de hacer esto es con MarshalIndent, que le permitirá especificar cómo le gustaría sangrar a través del indentargumento. Por lo tanto, json.MarshalIndent(data, "", " ")se imprimirá con cuatro espacios para la sangría.

Alexander Bauer
fuente
17
Sí, eso parece justo: ya está incorporado, solo queda incluir la palabra clave "pretty-print" en el paquete doc para que el siguiente tipo de búsqueda lo encuentre. (Dejará una nota de retroalimentación para los encargados del mantenimiento del documento). ¡Gracias!
Brad Peabody
39
json.MarshalIndent(data, "", "\t")si quieres pestañas
Kyle Brandt
82
json.MarshalIndent(data, "", "🐱")si quieres gatos lo siento
briiC
46
json.MarshalIndent(data, "", "\t🐱")si quieres ... gatos atigrados ... lo siento
Davos
78

La respuesta aceptada es excelente si tiene un objeto que desea convertir en JSON. La pregunta también menciona la impresión bonita de cualquier cadena JSON, y eso es lo que estaba tratando de hacer. Solo quería registrar bastante JSON de una solicitud POST (específicamente un informe de violación de CSP ).

Para usar MarshalIndent, tendrías Unmarshalque convertirlo en un objeto. Si necesitas eso, hazlo, pero yo no. Si solo necesita imprimir una matriz de bytes, Indentes su amigo.

Esto es lo que terminé con:

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

Para un mejor uso de la memoria, supongo que esto es mejor:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}
mh-cbon
fuente
¿Se SetIndentagregó recientemente? Es esencialmente desconocido para la mayoría.
chappjc
1
Aparentemente, @chappjc SetIndent(originalmente nombrado Indent) se agregó en marzo de 2016 y se lanzó en Go 1.7, que fue aproximadamente 3 años después de que se formulara originalmente esta pregunta: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu
19

Estaba frustrado por la falta de una forma rápida y de alta calidad para ordenar JSON a una cadena coloreada en Go, así que escribí mi propio Marshaller llamado ColorJSON .

Con él, puede producir fácilmente resultados como este usando muy poco código:

Salida de muestra de 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))
}

Estoy escribiendo la documentación para ello ahora, pero estaba emocionado de compartir mi solución.

Tyler Brock
fuente
17

Editar Mirando hacia atrás, esto no es idiomático Go. Las funciones de ayuda pequeñas como esta agregan un paso adicional de complejidad. En general, la filosofía Go prefiere incluir las 3 líneas simples en lugar de 1 línea difícil.


Como @robyoder mencionó, json.Indentes el camino a seguir. Pensé que agregaría esta pequeña prettyprintfunción:

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

jpillora
fuente
7

Esto es lo que uso. Si no puede imprimir bastante el JSON, solo devuelve la cadena original. Útil para imprimir respuestas HTTP que deberían contener 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
fuente
6

Aquí está mi solución :

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

Una simple impresora lista para usar en Go. Se puede compilar a un binario a través de:

go build -o jsonformat jsonformat.go

Se lee desde la entrada estándar, escribe en la salida estándar y permite configurar la sangría:

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 ejecutar comandos bash como:

cat myfile | jsonformat | grep "key"
Paweł Szczur
fuente
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 se ve

Clare Chu
fuente
1

Soy algo nuevo para ir, pero esto es lo que he reunido hasta ahora:

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 es la ejecución de la función, y solo estándar

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