Як можна гарненько роздрукувати JSON за допомогою Go?


191

Хтось знає про простий спосіб гарненько роздрукувати вихід JSON в Go?

Пакет http://golang.org/pkg/encoding/json/ , схоже, не містить функціональності для цього (EDIT: так, див. Прийняту відповідь), і швидкий google не виявляє нічого очевидного.

Використання, які я шукаю, - це друк результатів json.Marshalі просто форматування рядка, повного JSON, звідки завгодно, тому його легше читати з метою налагодження.


Попередження: у моїх експериментах у словниках JSON рядки-індекси повинні бути укладені в дужки. Отже, {name: "value"}не буде гаразд, незважаючи на те, що більшість інтерпретаторів Javascript це використовує . Тільки {"name": "value"} буде працювати з функціями бібліотеки Go JSON.
peterh

2
@peterh Я думаю, що ви плутаєте буквальний синтаксис JavaScript з власним JSON. Специфікація JSON ( json.org ) чітко вказує на те, що дозволені лише рядкові літерали (тобто це потребує лапок), тоді як синтаксис об'єкта мови JS не має цього обмеження. Бібліотека Go дотримується специфікації.
Бред Пібоді

Відповіді:


296

Під гарним шрифтом, я припускаю, що ви маєте на увазі відступ, як так

{
    "data": 1234
}

а не

{"data":1234}

Найпростіший спосіб зробити це з допомогою MarshalIndent, який дозволить вам вказати, як ви хотіли б це з відступом через indentаргумент. Таким чином, json.MarshalIndent(data, "", " ")буде сильно друкувати, використовуючи чотири пробіли для відступу.


17
Так, це виглядає просто як річ - вона вже вбудована, залишилося лише включити ключове слово "симпатичний друк" в документ pkg, щоб наступний хлопець, що шукає, знайшов його. (Залишимо записку про зворотній зв'язок для технічного обслуговування.) Tks!
Бред Пібоді

39
json.MarshalIndent(data, "", "\t")якщо ви хочете вкладки.
Кайл Брандт

80
json.MarshalIndent(data, "", "🐱")якщо ви хочете котів. вибачте
briiC

45
json.MarshalIndent(data, "", "\t🐱")якщо ви хочете ... коти з таббі ... вибачте
Давос

78

Прийнята відповідь чудова, якщо у вас є об’єкт, який ви хочете перетворити на JSON. Питання також згадує про гарне друкування будь-якої струни JSON, і це те, що я намагався зробити. Я просто хотів гарненько записати JSON із запиту POST (зокрема, повідомлення про порушення CSP ).

Для використання MarshalIndentвам доведеться це Unmarshalзробити в об’єкт. Якщо вам це потрібно, ідіть, але я цього не зробив. Якщо вам просто потрібно сильно роздрукувати байтовий масив, Indentваш друг - звичайний .

Ось що я закінчив:

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

48

Для кращого використання пам'яті, я думаю, це краще:

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

Нещодавно SetIndentдодали? Це майже невідомо більшості.
chappjc

1
Очікується, що @chappjc SetIndent(спочатку названий Indent) був доданий березень 2016 року та випущений у програмі Go 1.7, що пройшло приблизно через 3 роки після того, як це питання було спочатку задано: github.com/golang/go/commit/… github.com/golang/go/commit/ …
aoeu

19

Мене розчарувало відсутність швидкого, якісного способу маршалку JSON до розфарбованої струни в Go, тому я написав власний маршаллер під назвою ColorJSON .

З його допомогою ви можете легко отримати такий вихід, використовуючи дуже мало коду:

Вихід зразка 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))
}

Я зараз пишу документацію на це, але з радістю поділився своїм рішенням.


17

Редагувати Озираючись назад, це не ідіоматично. Невеликі допоміжні функції, як це, додають додатковий крок складності. В цілому філософія Go вважає за краще включати 3 простих рядка над 1 хитрою лінією.


Як згадував @robyoder, json.Indentце шлях. Думав, я додаю цю маленьку prettyprintфункцію:

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


7

Ось що я використовую. Якщо він не вдається сильно роздрукувати JSON, він просто поверне початковий рядок. Корисно для друку відповідей HTTP, які повинні містити 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()
}

6

Ось моє рішення :

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
}

2

Простий з принтером симпатичний принтер у Go. Можна скласти його до двійкового файлу через:

go build -o jsonformat jsonformat.go

Він читає зі стандартного вводу, записує на стандартний вихід і дозволяє встановлювати відступи:

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

Це дозволяє запускати bash команди типу:

cat myfile | jsonformat | grep "key"

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

Як це виглядає


1

Я щось нове, але ось що я зібрав поки що:

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
}

Це виконання функції, і просто стандартне

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

Код:

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)

}

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))
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.