Comment puis-je imprimer JSON avec Go?

203

Quelqu'un connaît-il un moyen simple d'imprimer une sortie JSON dans Go?

Le package stock http://golang.org/pkg/encoding/json/ ne semble pas inclure de fonctionnalité pour cela (EDIT: c'est le cas, voir la réponse acceptée) et un rapide google ne révèle rien d'évident.

Les utilisations que je recherche sont à la fois l'impression du résultat json.Marshalet le formatage d'une chaîne pleine de JSON de n'importe où, ce qui facilite la lecture à des fins de débogage.

Brad Peabody
la source
Attention: sur mes expériences, dans les dictionnaires JSON les index de chaînes doivent être mis entre parenthèses. Donc, {name: "value"}ça ne va pas, malgré que la plupart des interpréteurs Javascript l'utilisent . Seul {"name": "value"} fonctionnera avec les fonctions de la bibliothèque Go JSON.
peterh - Réintégrer Monica
3
@peterh Je pense que vous confondez la syntaxe littérale JavaScript avec JSON proprement dit. La spécification JSON ( json.org ) indique clairement que seuls les littéraux de chaîne sont autorisés (ce qui signifie qu'il faut des guillemets), tandis que la syntaxe des objets de langage JS n'a pas cette restriction. La bibliothèque Go suit les spécifications.
Brad Peabody

Réponses:

316

Par joli imprimé, je suppose que vous voulez dire en retrait, comme ça

{
    "data": 1234
}

plutôt que

{"data":1234}

Le moyen le plus simple de le faire est avec MarshalIndent, qui vous permettra de spécifier comment vous souhaitez le mettre en retrait via l' indentargument. Ainsi, json.MarshalIndent(data, "", " ")joliment imprimer en utilisant quatre espaces pour l'indentation.

Alexander Bauer
la source
22
Ouais, cela ressemble exactement à la chose - c'est déjà intégré, il ne reste plus qu'à inclure le mot-clé "pretty-print" dans le document pkg pour que le prochain chercheur le trouve. (Laissera une note de retour pour les responsables de la documentation.) Tks!
Brad Peabody
50
json.MarshalIndent(data, "", "\t")si vous voulez des onglets.
Kyle Brandt
100
json.MarshalIndent(data, "", "🐱")si vous voulez des chats. désolé
briiC
65
json.MarshalIndent(data, "", "\t🐱")si vous voulez ... chats tabby ... désolé
Davos
89

La réponse acceptée est excellente si vous avez un objet que vous souhaitez transformer en JSON. La question mentionne également la jolie impression de n'importe quelle chaîne JSON, et c'est ce que j'essayais de faire. Je voulais juste enregistrer un peu de JSON à partir d'une requête POST (en particulier un rapport de violation CSP ).

Pour l'utiliser MarshalIndent, vous auriez à Unmarshalcela dans un objet. Si vous en avez besoin, allez-y, mais je ne l'ai pas fait. Si vous avez juste besoin d'imprimer un tableau d'octets, plain Indentest votre ami.

Voici ce que j'ai fini avec:

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
la source
50

Pour une meilleure utilisation de la mémoire, je suppose que c'est mieux:

var out io.Writer
enc := json.NewEncoder(out)
enc.SetIndent("", "    ")
if err := enc.Encode(data); err != nil {
    panic(err)
}
mh-cbon
la source
Avez- SetIndentvous été ajouté récemment? C'est essentiellement inconnu de la plupart.
chappjc
2
@chappjc SetIndent(nommé à l'origine Indent) a apparemment été ajouté en mars 2016 et publié dans Go 1.7, soit environ 3 ans après que cette question ait été posée à l'origine: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu
20

J'étais frustré par le manque d'un moyen rapide et de haute qualité de rassembler JSON en une chaîne colorisée dans Go, alors j'ai écrit mon propre Marshaller appelé ColorJSON .

Avec lui, vous pouvez facilement produire une sortie comme celle-ci en utilisant très peu de code:

Exemple de sortie 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))
}

J'écris la documentation pour cela maintenant mais j'étais ravi de partager ma solution.

Tyler Brock
la source
17

Edit Avec le recul, c'est Go non idiomatique. De petites fonctions d'aide comme celle-ci ajoutent une étape supplémentaire de complexité. En général, la philosophie Go préfère inclure les 3 lignes simples sur 1 ligne délicate.


Comme @robyoder l'a mentionné, json.Indentc'est la voie à suivre. Je pensais ajouter cette petite prettyprintfonction:

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
la source
7

Voici ce que j'utilise. S'il ne parvient pas à imprimer correctement le JSON, il renvoie simplement la chaîne d'origine. Utile pour imprimer des réponses HTTP qui doivent contenir 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
la source
6

Voici ma 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
}
Raed Shomali
la source
2

Une jolie imprimante simple prête à l'emploi dans Go. On peut le compiler en binaire via:

go build -o jsonformat jsonformat.go

Il lit à partir de l'entrée standard, écrit sur la sortie standard et permet de définir l'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)
    }
}

Il permet d'exécuter des commandes bash comme:

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

À quoi ça ressemble

Clare Chu
la source
1

Je suis un peu nouveau, mais c'est ce que j'ai compris jusqu'à présent:

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
}

C'est l'exécution de la fonction, et juste 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
la source
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
la source