Как красиво отформатировать JSON в Go: подробное руководство
Основное решение: json.MarshalIndent
В Go для красивого форматирования JSON существует встроенная функция json.MarshalIndent из пакета encoding/json. Она принимает три аргумента: данные для сериализации, префикс (обычно пустая строка) и отступ (чаще всего табуляция или два пробела).
Исправленный пример из вашего кода:
package main
import (
"encoding/json"
"fmt"
)
type User struct {
Name string `json:"name"`
Age int `json:"age"`
Email string `json:"email"`
Address Address `json:"address"`
}
type Address struct {
City string `json:"city"`
Country string `json:"country"`
}
func main() {
user := User{
Name: "Иван Иванов",
Age: 30,
Email: "ivan@example.com",
Address: Address{
City: "Москва",
Country: "Россия",
},
}
// Используем json.MarshalIndent
jsonData, err := json.MarshalIndent(user, "", " ") // отступ — 2 пробела
if err != nil {
fmt.Println("Ошибка:", err)
return
}
fmt.Println(string(jsonData))
}
Результат:
{
"name": "Иван Иванов",
"age": 30,
"email": "ivan@example.com",
"address": {
"city": "Москва",
"country": "Россия"
}
}
Параметры MarshalIndent
- Первый параметр (data) — любые данные, которые можно сериализовать в JSON (структура, map, slice и т.д.).
- Второй параметр (prefix) — строка, которая добавляется в начало каждой строки. Обычно пустая строка
"". Можно использовать, например,"> "для создания отступа в виде символов. - Третий параметр (indent) — строка отступа для каждого уровня вложенности. Чаще всего
" "(два пробела) или"\t"(табуляция).
Пример с табуляцией
jsonData, _ := json.MarshalIndent(user, "", "\t")
fmt.Println(string(jsonData))
Результат:
{
"name": "Иван Иванов",
"age": 30,
"email": "ivan@example.com",
"address": {
"city": "Москва",
"country": "Россия"
}
}
Запись в файл с форматированием
package main
import (
"encoding/json"
"os"
)
type Config struct {
Port int `json:"port"`
Host string `json:"host"`
}
func main() {
cfg := Config{Port: 8080, Host: "localhost"}
file, err := os.Create("config.json")
if err != nil {
panic(err)
}
defer file.Close()
encoder := json.NewEncoder(file)
encoder.SetIndent("", " ") // устанавливаем отступы для encoder
if err := encoder.Encode(cfg); err != nil {
panic(err)
}
}
Pretty-print для логов
package main
import (
"encoding/json"
"log"
"bytes"
)
func main() {
data := map[string]interface{}{
"message": "Hello",
"level": "info",
"data": []int{1, 2, 3},
}
var buf bytes.Buffer
encoder := json.NewEncoder(&buf)
encoder.SetIndent("", " ")
encoder.Encode(data)
log.Println(buf.String())
}
Важные замечания
- Для красивого вывода используйте теги
json:"fieldname"в структурах, чтобы управлять именами полей в JSON (как показано в примере выше). - Функция
MarshalIndentработает медленнее, чемMarshal, поэтому для production-кода, где производительность критична, используйте обычныйMarshal, а pretty-print применяйте только для отладки. - Для больших объёмов данных используйте
json.EncoderсSetIndent, чтобы не держать весь JSON в памяти.
Альтернативный подход: использование json.Encoder с кастомизацией
Если вы работаете с потоками данных или хотите больше контроля над форматированием, используйте json.NewEncoder. Он позволяет задавать отступы через метод SetIndent и писать JSON напрямую в io.Writer (консоль, файл, буфер).
package main
import (
"encoding/json"
"os"
)
func main() {
user := map[string]interface{}{
"name": "Иван",
"age": 30,
"address": map[string]string{
"city": "Москва",
},
}
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ") // 4 пробела
encoder.Encode(user)
}
Результат:
{
"address": {
"city": "Москва"
},
"age": 30,
"name": "Иван"
}
Когда использовать этот подход
- Когда нужно выводить JSON в лог или в ответ HTTP-запроса без предварительного создания строки.
- Когда данные большие и не помещаются в память —
Encoderработает потоково. - Если нужно добавить префикс (например, для логов с меткой времени).
Недостаток: Encoder.Encode автоматически добавляет символ новой строки \n в конце, что может быть нежелательно при записи в файл с другим форматированием. В таких случаях используйте MarshalIndent.