JSON Formatter Go — MarshalIndent() Rehberi

·Systems Engineer·İnceleyenTobias Müller·Yayınlandı

Ücretsiz JSON Formatlayıcı ve Güzelleştirici aracını doğrudan tarayıcınızda kullanın — kurulum gerektirmez.

JSON Formatlayıcı ve Güzelleştirici Online Dene →

Bir Go mikroservisi üzerinde çalışırken API yanıtını veya bir yapılandırma dosyasını incelemem gerektiğinde, kompakt JSON ilk engel oluyor — yüzlerce iç içe alan içeren tek satır bakışta neredeyse hiçbir şey anlatmıyor. Go'da JSON biçimlendirmek için standart kütüphane ihtiyacınız olan her şeyi sunar: json.MarshalIndent zaten encoding/json içinde yer alır, her Go kurulumunda gelir ve sıfır üçüncü taraf bağımlılığı gerektirir. Bu kılavuz şunları kapsar: struct tag'ler, özel MarshalJSON() uygulamaları, ham baytları yeniden biçimlendirmek için json.Indent, json.Decoder ile büyük dosyaları akış olarak işleme, yüksek verimli yollar için go-json ne zaman kullanmalı ve terminalde hızlı biçimlendirme için CLI komutları. Tüm örnekler Go 1.21+ kullanır.

  • json.MarshalIndent(v, "", "\t") standart kütüphanedir — sıfır bağımlılık, her Go kurulumunda gelir.
  • Struct tag'leri json:"field_name,omitempty" serializasyon anahtarlarını kontrol eder ve sıfır değerli alanları çıktıdan atlar.
  • MarshalJSON() metodunu herhangi bir türe uygulayarak JSON temsilini tamamen kontrol edebilirsiniz.
  • json.Indent(), marshal edilmiş []byte'ı struct'ı yeniden ayrıştırmadan yeniden biçimlendirir — ham baytlar için daha hızlıdır.
  • 100 MB'dan büyük dosyalar için: hepsini belleğe yüklemeden kayıtları akış olarak işlemek için json.Decoder ile Token() kullanın.
  • go-json, yüksek verimli API'lar için encoding/json'dan 3–5× daha hızlı bir drop-in alternatifidir.

JSON Biçimlendirme Nedir?

JSON biçimlendirme — güzel yazdırma olarak da adlandırılır — kompakt, küçültülmüş bir JSON string'ini tutarlı girinti ve satır sonlarıyla insan tarafından okunabilir bir düzene dönüştürür. Temel veri aynıdır; yalnızca boşluk değişir. Kompakt JSON, her baytın önemli olduğu ağ transferleri için idealdir; biçimlendirilmiş JSON ise debug, kod incelemesi, log denetimi ve yapılandırma dosyası yazımı için idealdir. Go'nun encoding/json paketi her iki modu tek bir fonksiyon çağrısıyla yönetir — json.Marshal ve json.MarshalIndent arasında geçiş yaparak kompakt ve girintili çıktı seçeneği sunulur.

Before · json
After · json
{"service":"payments","port":8443,"workers":4}
{
	"service": "payments",
	"port": 8443,
	"workers": 4
}

json.MarshalIndent() — Standart Kütüphane Yaklaşımı

json.MarshalIndent Go standart kütüphanesinin bir parçası olan encoding/json paketinde yer alır — go get gerekmez. İmzası MarshalIndent(v any, prefix, indent string) ([]byte, error) şeklindedir: prefix string'i her çıktı satırının başına eklenir (neredeyse her zaman boş bırakılır) ve indent her iç içe geçme seviyesinde bir kez tekrarlanır. Sekmeler için "\t" veya iki boşluk için " " geçirin.

Go — minimal çalışan örnek
package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type ServiceConfig struct {
	Host       string   `json:"host"`
	Port       int      `json:"port"`
	Workers    int      `json:"workers"`
	TLSEnabled bool     `json:"tls_enabled"`
	AllowedIPs []string `json:"allowed_ips"`
}

func main() {
	cfg := ServiceConfig{
		Host:       "payments-api.internal",
		Port:       8443,
		Workers:    8,
		TLSEnabled: true,
		AllowedIPs: []string{"10.0.0.0/8", "172.16.0.0/12"},
	}

	data, err := json.MarshalIndent(cfg, "", "	")
	if err != nil {
		log.Fatalf("marshal config: %v", err)
	}
	fmt.Println(string(data))
}
// {
// 	"host": "payments-api.internal",
// 	"port": 8443,
// 	"workers": 8,
// 	"tls_enabled": true,
// 	"allowed_ips": [
// 		"10.0.0.0/8",
// 		"172.16.0.0/12"
// 	]
// }

Sekme ve boşluk arasındaki seçim büyük ölçüde ekip kurallarına bağlıdır. Birçok Go projesi sekmeleri tercih eder çünkü gofmt (Go kaynak kodunu biçimlendiren araç) sekme kullanır. JSON bir JavaScript veya Python tüketicisine gidecekse iki veya dört boşluklu girinti yaygındır. Aynı struct ile her iki girinti stili yan yana:

Go — sekme ve boşluk karşılaştırması
// Sekme girintisi — Go araçlarında tercih edilir
data, _ := json.MarshalIndent(cfg, "", "	")
// {
// 	"host": "payments-api.internal",
// 	"port": 8443
// }

// İki boşluklu girinti — JS/Python tüketicili API'lar için yaygın
data, _ = json.MarshalIndent(cfg, "", "  ")
// {
//   "host": "payments-api.internal",
//   "port": 8443
// }

// Dört boşluklu girinti
data, _ = json.MarshalIndent(cfg, "", "    ")
// {
//     "host": "payments-api.internal",
//     "port": 8443
// }
Not:Kompakt çıktıya ihtiyaç duyduğunuzda —ağ yükleri, önbellek değerleri veya ikili boyutun önemli olduğu herhangi bir yol için — json.Marshal(v) kullanın. Aynı değer argümanını alır ve aynı hata semantiğine sahiptir, ancak boşluk içermeyen tek satır JSON üretir.

Struct Tag'leri — Alan Adlarını ve Omitempty'yi Kontrol Etme

Go struct tag'leri, alan bildirimlerinin arkasına yerleştirilen ve encoding/json'a her alanı nasıl serileştireceğini söyleyen string literallerdir. Üç temel yönerge vardır: json:"name" çıktıdaki alan adını değiştirir, omitempty alan türünün sıfır değerini tuttuğunda alanı atlar ve json:"-" alanı tamamen hariç tutar — şifreler, dahili tanımlayıcılar veya servis sınırını asla geçmemesi gereken alanlar için kullanışlıdır.

Go — API yanıtı için struct tag'leri
type UserProfile struct {
	ID          string  `json:"id"`
	Email       string  `json:"email"`
	DisplayName string  `json:"display_name,omitempty"`  // boş string ise atla
	AvatarURL   *string `json:"avatar_url,omitempty"`    // nil pointer ise atla
	IsAdmin     bool    `json:"is_admin,omitempty"`      // false ise atla
	passwordHash string                                   // dışa aktarılmamış — otomatik hariç
}

// Tüm opsiyonel alanlar dolu olan kullanıcı
full := UserProfile{
	ID: "usr_7b3c", Email: "ops@ornek.com.tr",
	DisplayName: "Ops Ekibi", IsAdmin: true,
}
// {
//   "id": "usr_7b3c",
//   "email": "ops@ornek.com.tr",
//   "display_name": "Ops Ekibi",
//   "is_admin": true
// }

// Opsiyonel alanı olmayan kullanıcı — tamamen atlanırlar
minimal := UserProfile{ID: "usr_2a91", Email: "dev@ornek.com.tr"}
// {
//   "id": "usr_2a91",
//   "email": "dev@ornek.com.tr"
// }

json:"-" tag'i, değerinden bağımsız olarak koşulsuz biçimde hariç tutulması gereken alanlar için doğru seçimdir — genellikle sırlar, dahili takip alanları veya bellekte doğru olup herhangi bir harici sisteme serileştirilmemesi gereken veriler.

Go — hassas alanları hariç tutma
type AuthToken struct {
	TokenID      string `json:"token_id"`
	Subject      string `json:"sub"`
	IssuedAt     int64  `json:"iat"`
	ExpiresAt    int64  `json:"exp"`
	SigningKey    []byte `json:"-"`   // hiçbir zaman serileştirilmez
	RefreshToken string `json:"-"`   // hiçbir zaman serileştirilmez
}

tok := AuthToken{
	TokenID: "tok_8f2a", Subject: "usr_7b3c",
	IssuedAt: 1741614120, ExpiresAt: 1741617720,
	SigningKey: []byte("gizli"), RefreshToken: "rt_9e4f",
}
data, _ := json.MarshalIndent(tok, "", "  ")
// {
//   "token_id": "tok_8f2a",
//   "sub": "usr_7b3c",
//   "iat": 1741614120,
//   "exp": 1741617720
// }
// SigningKey ve RefreshToken hiç görünmez
Not:Dışa aktarılmamış (küçük harf) struct alanları, encoding/json tarafından herhangi bir etiket olmaksızın her zaman hariç tutulur. Dışa aktarılmamış alanlara json:"-" eklemenize gerek yoktur — hariç tutma otomatiktir ve geçersiz kılınamaz.

Özel MarshalJSON() — Standart Olmayan Türleri İşleme

Herhangi bir Go türü, MarshalJSON() ([]byte, error) metodunu tanımlayarak json.Marshaler arayüzünü uygulayabilir. encoding/json böyle bir tiple karşılaştığında, varsayılan yansıma tabanlı marshal yerine bu metodu çağırır. Bu, belirli bir kablo temsili gerektiren domain türleri için kanonik Go kalıbıdır — parasal değerler, durum enum'ları, özel zaman biçimleri veya veriyi serileştirilmesi gerekenden farklı şekilde depolayan herhangi bir tür.

Özel Tür — Kuruş-Ondalık Dönüşümlü Para

Go — Para için özel MarshalJSON
package main

import (
	"encoding/json"
	"fmt"
	"log"
)

type Money struct {
	Amount   int64  // kayan nokta kaymasını önlemek için kuruş olarak saklanır
	Currency string
}

func (m Money) MarshalJSON() ([]byte, error) {
	return json.Marshal(struct {
		Amount   float64 `json:"amount"`
		Currency string  `json:"currency"`
		Display  string  `json:"display"`
	}{
		Amount:   float64(m.Amount) / 100,
		Currency: m.Currency,
		Display:  fmt.Sprintf("%s %.2f", m.Currency, float64(m.Amount)/100),
	})
}

type Invoice struct {
	ID       string `json:"id"`
	Subtotal Money  `json:"subtotal"`
	Tax      Money  `json:"tax"`
	Total    Money  `json:"total"`
}

func main() {
	inv := Invoice{
		ID:       "inv_9a2f91bc",
		Subtotal: Money{Amount: 19900, Currency: "TRY"},
		Tax:      Money{Amount: 1592, Currency: "TRY"},
		Total:    Money{Amount: 21492, Currency: "TRY"},
	}
	data, err := json.MarshalIndent(inv, "", "  ")
	if err != nil {
		log.Fatalf("marshal invoice: %v", err)
	}
	fmt.Println(string(data))
}
// {
//   "id": "inv_9a2f91bc",
//   "subtotal": { "amount": 199, "currency": "TRY", "display": "TRY 199.00" },
//   "tax":      { "amount": 15.92, "currency": "TRY", "display": "TRY 15.92" },
//   "total":    { "amount": 214.92, "currency": "TRY", "display": "TRY 214.92" }
// }

Durum Enum — String Temsili

Go — durum enum için özel MarshalJSON
type OrderStatus int

const (
	StatusPending OrderStatus = iota
	StatusPaid
	StatusShipped
	StatusCancelled
)

var orderStatusNames = map[OrderStatus]string{
	StatusPending:   "beklemede",
	StatusPaid:      "odendi",
	StatusShipped:   "gonderildi",
	StatusCancelled: "iptal_edildi",
}

func (s OrderStatus) MarshalJSON() ([]byte, error) {
	name, ok := orderStatusNames[s]
	if !ok {
		return nil, fmt.Errorf("bilinmeyen sipariş durumu: %d", s)
	}
	return json.Marshal(name)
}

type Order struct {
	ID     string      `json:"id"`
	Status OrderStatus `json:"status"`
}

o := Order{ID: "ord_3c7f", Status: StatusShipped}
data, _ := json.MarshalIndent(o, "", "  ")
// {
//   "id": "ord_3c7f",
//   "status": "gonderildi"
// }
Not:MarshalJSON() ve UnmarshalJSON()'ı her zaman birlikte uygulayın. Yalnızca marshal uygulanırsa, türün JSON üzerinden döngüsü (seri hale getir → depola → seri hale getirmeyi kaldır) sessiz sedasız yapıyı kaybedebilir veya yanlış türü döndürebilir. İkili, türün bir JSON round-trip'ten sağ çıkabileceğini garanti eden bir sözleşme oluşturur.

UUID — String Olarak Serileştirme

Go'nun standart kütüphanesinde UUID türü yoktur. En yaygın seçim, zaten MarshalJSON() uygulayan ve tırnaklı RFC 4122 string'i olarak serileştiren github.com/google/uuid'dir. Ham bir [16]byte veya özel ID türü kullanıyorsanız, JSON çıktınızda base64 kodlu binary blob'lardan kaçınmak için arayüzü kendiniz uygulayın.

Go 1.21+ — UUID serileştirme
import (
    "encoding/json"
    "fmt"

    "github.com/google/uuid"
)

type AuditEvent struct {
    EventID   uuid.UUID `json:"event_id"`   // "550e8400-e29b-41d4-a716-446655440000" olarak serileştirilir
    SessionID uuid.UUID `json:"session_id"`
    Action    string    `json:"action"`
    ActorID   string    `json:"actor_id"`
    OccuredAt string    `json:"occurred_at"`
}

event := AuditEvent{
    EventID:   uuid.New(),
    SessionID: uuid.MustParse("550e8400-e29b-41d4-a716-446655440000"),
    Action:    "user.password_changed",
    ActorID:   "usr_7f3a91bc",
    OccuredAt: "2026-03-10T14:22:00Z",
}

data, _ := json.MarshalIndent(event, "", "  ")
fmt.Println(string(data))
// {
//   "event_id": "a4b2c1d0-...",
//   "session_id": "550e8400-e29b-41d4-a716-446655440000",
//   "action": "user.password_changed",
//   "actor_id": "usr_7f3a91bc",
//   "occurred_at": "2026-03-10T14:22:00Z"
// }
Not:UUID'leri özel bir tür olmadan [16]byte olarak saklarsanız, encoding/jsonbunları base64 string'i olarak kodlar — ör. "VQ6EAOKbQdSnFkRmVUQAAA==". Her zaman uygun bir UUID türü kullanın ya da kanonik tire-ayraçlı string formatını yaymak için MarshalJSON() uygulayın.

json.MarshalIndent() Parametre Referansı

Fonksiyon imzası func MarshalIndent(v any, prefix, indent string) ([]byte, error). Hem prefix hem de indent string literalleridir — Python'ın indent=4 gibi sayısal kısaltma yoktur.

Parametre
Type
Açıklama
v
any
Dönüştürülecek değer — struct, map, slice veya primitive
prefix
string
Her çıktı satırının başına eklenen string (genellikle "")
indent
string
Her girinti seviyesi için kullanılan string ("\t" veya " ")

Yaygın struct tag seçenekleri:

Tag
Etki
json:"name"
JSON çıktısında alan adını name olarak değiştirir
json:"name,omitempty"
Yeniden adlandır + sıfır değerse atla (nil, "", 0, false)
json:"-"
Bu alanı JSON çıktısından her zaman hariç tutar
json:",string"
Sayı veya bool değerini JSON string olarak kodlar

json.Indent() — Mevcut JSON Baytlarını Yeniden Biçimlendirme

Bir HTTP yanıt gövdesinden, Postgres jsonb sütunundan veya os.ReadFile ile okunan bir dosyadan gelen []byte JSON'unuz varsa, güzel yazdırmadan önce struct tanımlayıp unmarshal etmenize gerek yoktur. json.Indent ham baytları doğrudan bir bytes.Buffer'a girintili çıktı yazarak yeniden biçimlendirir.

Go — ham baytlarda json.Indent
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
)

func main() {
	// Upstream servisin ham JSON yükünü simüle etme
	raw := []byte(`{"trace_id":"tr_9a2f","service":"checkout","latency_ms":342,"error":null}`)

	var buf bytes.Buffer
	if err := json.Indent(&buf, raw, "", "	"); err != nil {
		log.Fatalf("indent: %v", err)
	}
	fmt.Println(buf.String())
}
// {
// 	"trace_id": "tr_9a2f",
// 	"service": "checkout",
// 	"latency_ms": 342,
// 	"error": null
// }

Mikroservislerde sıkça kullandığım bir kalıp, yapılandırılmış loglara yazmadan önce json.Indent çağırmaktır — ihmal edilebilir ek yük ekler ve log girişlerini bir olay sırasında okumayı çok daha kolay hale getirir. Fonksiyon özellikle HTTP yanıtlarını loglama, depolanmış JSON string'lerini güzel yazdırma ve struct tanımının bulunmadığı format-on-read pipeline'larında kullanışlıdır.

Go — debug loglama için json.Indent
func logResponse(logger *slog.Logger, statusCode int, body []byte) {
	var pretty bytes.Buffer
	if err := json.Indent(&pretty, body, "", "  "); err != nil {
		// Gövde geçerli JSON değil — ham olarak logla
		logger.Debug("upstream yanıtı", "status", statusCode, "body", string(body))
		return
	}
	logger.Debug("upstream yanıtı", "status", statusCode, "body", pretty.String())
}
Uyarı:json.Indent, boşluk eklemek için yapısal olarak gerekenin ötesinde JSON'u tam olarak doğrulamaz. Tam sözdizimi doğrulaması için önce json.Valid(data) çağırın ve girinti denemesinden önce false durumunu ele alın.

Dosya ve HTTP Yanıtından JSON Biçimlendirme

Go servislerinde en yaygın iki gerçek dünya senaryosu, diskten okunan JSON dosyasını biçimlendirme (yapılandırma dosyaları, fikstür verileri, migrasyon tohumları) ve debug loglama veya test assertion'ları için HTTP yanıt gövdelerini güzel yazdırmadır. Her ikisi de aynı kalıbı izler: baytları okuyun, json.Indent çağırın veya unmarshal edip json.MarshalIndent yapın, ardından geri yazın veya loglayın.

Dosya Okuma → Biçimlendirme → Geri Yazma

Go — JSON dosyasını yerinde biçimlendirme
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"os"
)

func formatJSONFile(path string) error {
	data, err := os.ReadFile(path)
	if err != nil {
		return fmt.Errorf("oku %s: %w", path, err)
	}

	if !json.Valid(data) {
		return fmt.Errorf("%s içinde geçersiz JSON", path)
	}

	var buf bytes.Buffer
	if err := json.Indent(&buf, data, "", "	"); err != nil {
		return fmt.Errorf("girinti %s: %w", path, err)
	}

	if err := os.WriteFile(path, buf.Bytes(), 0644); err != nil {
		return fmt.Errorf("yaz %s: %w", path, err)
	}
	return nil
}

func main() {
	if err := formatJSONFile("config/database.json"); err != nil {
		log.Fatalf("format config: %v", err)
	}
	fmt.Println("config/database.json başarıyla biçimlendirildi")
}

HTTP Yanıtı → Decode → Debug Loglama için Güzel Yazdırma

Go — debug logu için HTTP yanıtını biçimlendirme
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
)

type HealthResponse struct {
	Status    string            `json:"status"`
	Version   string            `json:"version"`
	Checks    map[string]string `json:"checks"`
	UptimeSec int64             `json:"uptime_seconds"`
}

func main() {
	resp, err := http.Get("https://api.payments.internal/v2/health")
	if err != nil {
		log.Fatalf("health check: %v", err)
	}
	defer resp.Body.Close()

	var result HealthResponse
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		log.Fatalf("health yanıtını decode et: %v", err)
	}

	pretty, err := json.MarshalIndent(result, "", "  ")
	if err != nil {
		log.Fatalf("health yanıtını marshal et: %v", err)
	}
	fmt.Printf("Health check (%d):
%s
", resp.StatusCode, string(pretty))
}
// Health check (200):
// {
//   "status": "ok",
//   "version": "1.4.2",
//   "checks": {
//     "database": "ok",
//     "cache": "ok",
//     "queue": "degraded"
//   },
//   "uptime_seconds": 172800
// }

Ham Yanıt Gövdesi → json.Indent (Struct Gerektirmez)

Go — io.ReadAll ile ham HTTP gövdesini biçimlendirme
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
)

func main() {
	resp, err := http.Get("https://api.payments.internal/v2/health")
	if err != nil {
		log.Fatalf("istek: %v", err)
	}
	defer resp.Body.Close()

	body, err := io.ReadAll(resp.Body)
	if err != nil {
		log.Fatalf("gövdeyi oku: %v", err)
	}

	var buf bytes.Buffer
	if err := json.Indent(&buf, body, "", "  "); err != nil {
		log.Fatalf("indent: %v", err)
	}
	fmt.Println(buf.String())
}

Go'da HTTP Yanıtından JSON Güzel Yazdırma

Yukarıdaki iki yaklaşım en yaygın durumları kapsar: tipli struct'a decode edin ardından json.MarshalIndent çağırın (alanları doğrulamanız veya incelemeniz gerektiğinde en iyi seçenek) ya da io.ReadAll ile ham gövde baytlarını okuyun ve doğrudan json.Indent çağırın (struct tanımınız olmadığında hızlı debug loglama için en iyi seçenek). Ham-bayt yaklaşımı daha basittir ama Go tip güvenliği veya alan erişimi sağlamaz — saf bir görüntü dönüşümüdür. Her iki yaklaşım da, tam gövde belleğe sığdığı sürece büyük yanıt gövdelerini doğru şekilde işler.

Go — iki kalıp yan yana
// Kalıp A: tipli decode → MarshalIndent
// Belirli alanları incelemeniz veya doğrulamanız gerektiğinde kullanın
var result map[string]any
json.NewDecoder(resp.Body).Decode(&result)
pretty, _ := json.MarshalIndent(result, "", "  ")
fmt.Println(string(pretty))

// Kalıp B: ham baytlar → json.Indent
// Hızlı debug loglama için kullanın — struct tanımı gerekmez
body, _ := io.ReadAll(resp.Body)
var buf bytes.Buffer
json.Indent(&buf, body, "", "  ")
fmt.Println(buf.String())

Go Projelerinde Komut Satırı JSON Biçimlendirme

Bazen Go programı yazmadan terminalde JSON yükünü biçimlendirmeniz gerekir. Bu tek satırlıklar geliştirme ve olay müdahalesi sırasında kas belleğimde tuttuklarımdır.

bash — Python'ın yerleşik biçimlendiricisine JSON aktarma
echo '{"service":"payments","port":8443,"workers":4}' | python3 -m json.tool
# {
#     "service": "payments",
#     "port": 8443,
#     "workers": 4
# }
bash — tam özellikli biçimlendirme ve filtreleme için jq'ya aktarma
# Yalnızca biçimlendir
cat api-response.json | jq .

# İç içe alanı çıkart
cat api-response.json | jq '.checks.database'

# Bir diziyi filtrele
cat audit-log.json | jq '.[] | select(.severity == "error")'
bash — stdin üzerinden minimal Go main.go'ya aktarma
# main.go: stdin'den okur, biçimlendirir, stdout'a yazar
cat <<'EOF' > /tmp/fmt.go
package main
import ("bytes";"encoding/json";"io";"os")
func main() {
    b,_:=io.ReadAll(os.Stdin)
    var buf bytes.Buffer
    json.Indent(&buf,b,"","  ")
    os.Stdout.Write(buf.Bytes())
}
EOF
echo '{"port":8080,"debug":true}' | go run /tmp/fmt.go
Not:gofmt JSON değil Go kaynak kodunu biçimlendirir. JSON dosyalarını gofmt'e aktarmayın — ya hata verecek ya da tanınamaz çıktı üretecektir. JSON dosyaları için jq . veya python3 -m json.tool kullanın.

Yüksek Performanslı Alternatif — go-json

Go servislerinin büyük çoğunluğu için encoding/json yeterince hızlıdır. Ancak JSON marshal işlemi profil oluşturucunuzda görünüyorsa — yüksek verimli REST API'larda veya her istekte büyük yapılandırılmış log satırları yayımlayan servislerde yaygındır — go-json kütüphanesi, aynı API yüzeyiyle 3–5× daha hızlı çalışan drop-in bir alternatiftir.

bash — go-json kurulumu
go get github.com/goccy/go-json
Go — tek import değişikliğiyle encoding/json'ı go-json ile değiştirme
package main

import (
	// Bunu değiştirin:
	// "encoding/json"

	// Bununla — aynı API, başka kod değişikliği yok:
	json "github.com/goccy/go-json"

	"fmt"
	"log"
)

type AuditEvent struct {
	RequestID string `json:"request_id"`
	UserID    string `json:"user_id"`
	Action    string `json:"action"`
	ResourceID string `json:"resource_id"`
	IPAddress  string `json:"ip_address"`
	DurationMs int    `json:"duration_ms"`
}

func main() {
	event := AuditEvent{
		RequestID: "req_7d2e91", UserID: "usr_4421",
		Action: "invoice.download", ResourceID: "inv_9a2f",
		IPAddress: "203.0.113.45", DurationMs: 23,
	}
	data, err := json.MarshalIndent(event, "", "  ")
	if err != nil {
		log.Fatalf("marshal: %v", err)
	}
	fmt.Println(string(data))
	// Çıktı encoding/json ile aynı — yalnızca hız farklı
}

github.com/bytedance/sonic mevcut en hızlı Go JSON kütüphanesidir, ancak yalnızca amd64 ve arm64 üzerinde çalışır (JIT derleme kullanır). Taşınabilir bir drop-in'e ihtiyacınız olduğunda go-json kullanın; bilinen bir mimarideyseniz ve yoğun bir yolda her mikrosaniyeye ihtiyacınız varsa sonic'e uzanın.

Büyük JSON Dosyalarıyla Çalışma

json.MarshalIndent ve json.Indent her ikisi de tüm yükün bellekte olmasını gerektirir. 100 MB'ın üzerindeki dosyalar için — veri dışa aktarmaları, denetim günlükleri, Kafka tüketici yükleri — girdi akışı için json.Decoder kullanın ve kayıtları tek tek işleyin.

json.Decoder ile Büyük JSON Dizisini Akış Olarak İşleme

Go — büyük JSON dizisini belleğe yüklemeden akış olarak işleme
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"os"
)

type AuditEvent struct {
	RequestID  string `json:"request_id"`
	UserID     string `json:"user_id"`
	Action     string `json:"action"`
	Severity   string `json:"severity"`
	DurationMs int    `json:"duration_ms"`
}

func processAuditLog(path string) error {
	file, err := os.Open(path)
	if err != nil {
		return fmt.Errorf("aç %s: %w", path, err)
	}
	defer file.Close()

	dec := json.NewDecoder(file)

	// Açılış '[' token'ını oku
	if _, err := dec.Token(); err != nil {
		return fmt.Errorf("açılış token'ı oku: %w", err)
	}

	var processed int
	for dec.More() {
		var event AuditEvent
		if err := dec.Decode(&event); err != nil {
			return fmt.Errorf("event %d'yi decode et: %w", processed, err)
		}
		// Aynı anda bir event işle — sabit bellek kullanımı
		if event.Severity == "error" {
			fmt.Printf("[HATA] %s: %s (%dms)
", event.UserID, event.Action, event.DurationMs)
		}
		processed++
	}
	fmt.Printf("%d denetim eventi işlendi
", processed)
	return nil
}

func main() {
	if err := processAuditLog("audit-2026-03.json"); err != nil {
		log.Fatalf("denetim logunu işle: %v", err)
	}
}

NDJSON — Satır Başına Bir JSON Nesnesi

Go — NDJSON log akışını satır satır işleme
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"os"
)

type LogLine struct {
	Timestamp  string `json:"ts"`
	Level      string `json:"level"`
	Service    string `json:"service"`
	Message    string `json:"msg"`
	TraceID    string `json:"trace_id"`
	DurationMs int    `json:"duration_ms,omitempty"`
}

func main() {
	file, err := os.Open("service-2026-03.ndjson")
	if err != nil {
		log.Fatalf("logu aç: %v", err)
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	scanner.Buffer(make([]byte, 1024*1024), 1024*1024) // satır başına 1 MB

	for scanner.Scan() {
		var line LogLine
		if err := json.Unmarshal(scanner.Bytes(), &line); err != nil {
			continue // bozuk satırları atla
		}
		if line.Level == "error" {
			fmt.Printf("%s [%s] %s trace=%s
",
				line.Timestamp, line.Service, line.Message, line.TraceID)
		}
	}
	if err := scanner.Err(); err != nil {
		log.Fatalf("tara: %v", err)
	}
}
Not:JSON dosyanız 100 MB'ı aştığında veya sınırsız akışları işlerken (Kafka tüketicileri, log pipeline'ları, S3 nesne okumaları) akışa geçin. os.ReadFileile 500 MB'lık JSON dosyası yüklemek tüm bu tamponu heap'te tahsis eder, GC baskısını tetikler ve bellek kısıtlı container'larda OOM'a neden olabilir.

Yaygın Hatalar

MarshalIndent'ten dönen hatayı görmezden gelme

Sorun: Hatayı _ ile atmak, serileştirilemeyen bir değerin (kanal, fonksiyon, karmaşık sayı) sessiz sedasız nil çıktı üretmesi veya string(nil) çağrıldığında aşağı akışta panik yapması anlamına gelir.

Çözüm: Her zaman hatayı kontrol edin. Her zaman başarılı olması gereken bir türü marshal ediyorsanız, sessiz veri kaybından daha iyi olan panikleyen log.Fatalf tercih edilir.

Before · Go
After · Go
data, _ := json.MarshalIndent(payload, "", "  ")
fmt.Println(string(data)) // marshal başarısız olursa boş string
data, err := json.MarshalIndent(payload, "", "  ")
if err != nil {
    log.Fatalf("marshal payload: %v", err)
}
fmt.Println(string(data))
Binary çıktı için os.Stdout.Write yerine fmt.Println kullanma

Sorun: fmt.Println(string(data)), JSON'dan sonra bir satır sonu karakteri ekler; bu da çıktıyı ham bayt olarak işleyen pipeline'ları bozar — örneğin jq'ya aktarma veya binary protokole yazma.

Çözüm: Binary temiz çıktı için os.Stdout.Write(data) kullanın. İnsan görüntüsü için sonuna satır sonu gerekiyorsa açıkça ekleyin.

Before · Go
After · Go
data, _ := json.MarshalIndent(cfg, "", "  ")
fmt.Println(string(data)) // sona fazladan satır sonu ekler
data, _ := json.MarshalIndent(cfg, "", "  ")
os.Stdout.Write(data)
os.Stdout.Write([]byte("
")) // yalnızca gerektiğinde açık satır sonu
Pointer alanlarda omitempty unutma

Sorun: omitempty olmadan, nil *string veya *int pointer "field": null olarak serileştirilir. Bu dahili alan adlarını ifşa eder ve tüketici tarafında katı JSON şema doğrulayıcılarını bozabilir.

Çözüm: Çıktıda null değil yokluğunu istediğiniz pointer alanlara omitempty ekleyin. omitempty ile nil *T, JSON'da hiç anahtar üretmez.

Before · Go
After · Go
type WebhookPayload struct {
    EventID   string  `json:"event_id"`
    ErrorMsg  *string `json:"error_msg"`  // nil iken null olarak görünür
}
// {"event_id":"evt_3c7f","error_msg":null}
type WebhookPayload struct {
    EventID   string  `json:"event_id"`
    ErrorMsg  *string `json:"error_msg,omitempty"`  // nil iken atlanır
}
// {"event_id":"evt_3c7f"}
Struct yerine map[string]interface{} kullanma

Sorun: map[string]any içine unmarshal etmek tip bilgisini kaybeder, manuel tip doğrulaması gerektirir ve belirleyici olmayan anahtar sırası üretir — JSON diff'lerini ve log karşılaştırmalarını zorlaştırır.

Çözüm: Uygun json tag'leriyle struct tanımlayın. Struct'lar tip güvenli, daha hızlı marshal edilir, struct tanımına uyan belirleyici alan sırası üretir ve kodu kendi kendini belgeler hale getirir.

Before · Go
After · Go
var result map[string]any
json.Unmarshal(body, &result)
port := result["port"].(float64) // tip doğrulaması gerekli, yanlış tipte panik yapar
type ServiceStatus struct {
    Service string `json:"service"`
    Port    int    `json:"port"`
    Healthy bool   `json:"healthy"`
}
var result ServiceStatus
json.Unmarshal(body, &result)
port := result.Port // tipli, güvenli, hızlı

encoding/json ve Alternatifler — Hızlı Karşılaştırma

Yöntem
Düzenlenmiş Çıktı
Geçerli JSON
Özel Tipler
Akış
Kurulum Gerektirir
json.MarshalIndent
✓ MarshalJSON ile
Hayır (stdlib)
json.Indent
Yok (sadece byte)
Hayır (stdlib)
json.Encoder
✗ (kompakt)
✓ MarshalJSON ile
Hayır (stdlib)
go-json
go get
sonic
go get (amd64/arm64)
jq (CLI)
Yok
Sistem kurulumu

Struct tanımını kontrol ettiğiniz ve biçimlendirilmiş çıktıya ihtiyaç duyduğunuz her durum için json.MarshalIndent kullanın — yapılandırma dosyaları, debug loglama, test fikstürleri ve API yanıt loglama. Go tipleri üzerinden round-trip olmadan yalnızca boşluk eklemeniz gerektiğinde ham baytlarınız varsa json.Indent kullanın. go-json veya sonic'e yalnızca profilleyici JSON marshal işleminin ölçülebilir bir darboğaz olduğunu doğruladıktan sonra geçin — çoğu servis için standart kütüphane fazlasıyla yeterlidir.

Sıkça Sorulan Sorular

Go'da JSON nasıl güzel biçimlendirilir?

encoding/json paketindeki json.MarshalIndent(v, "", "\t") fonksiyonunu çağırın — ikinci argüman satır başı ön eki (genellikle boş), üçüncüsü ise girinti karakteridir. Sekme için "\t", iki boşluk için " " geçirin. Harici kütüphane gerekmez; encoding/json Go standart kütüphanesiyle birlikte gelir.

Go
package main

import (
	"encoding/json"
	"fmt"
	"log"
)

func main() {
	config := map[string]any{
		"service": "payments-api",
		"port":    8443,
		"region":  "eu-west-1",
	}
	data, err := json.MarshalIndent(config, "", "	")
	if err != nil {
		log.Fatalf("marshal: %v", err)
	}
	fmt.Println(string(data))
	// {
	// 	"port": 8443,
	// 	"region": "eu-west-1",
	// 	"service": "payments-api"
	// }
}

json.Marshal ile json.MarshalIndent arasındaki fark nedir?

json.Marshal, boşluk içermeyen kompakt tek satır JSON üretir — her baytın önemli olduğu ağ transferleri için idealdir. json.MarshalIndent ise iki ekstra string parametre alır (prefix ve indent) ve girintili, insan tarafından okunabilir çıktı üretir. Her iki fonksiyon da aynı değer türlerini kabul eder ve ([]byte, error) döndürür. MarshalIndent'in tek maliyeti biraz daha fazla bayt ve boşluk eklemek için ihmal edilebilir miktarda CPU'dur.

Go
import "encoding/json"

type HealthCheck struct {
	Status  string `json:"status"`
	Version string `json:"version"`
	Uptime  int    `json:"uptime_seconds"`
}

h := HealthCheck{Status: "ok", Version: "1.4.2", Uptime: 86400}

compact, _ := json.Marshal(h)
// {"status":"ok","version":"1.4.2","uptime_seconds":86400}

pretty, _ := json.MarshalIndent(h, "", "  ")
// {
//   "status": "ok",
//   "version": "1.4.2",
//   "uptime_seconds": 86400
// }

Struct unmarshal etmeden JSON []byte nasıl biçimlendirilir?

json.Indent(&buf, src, "", "\t") kullanın. Bu fonksiyon mevcut bir JSON []byte alır ve girintili versiyonu bytes.Buffer içine yazar — struct tanımına, tip doğrulamaya veya Go tipleri üzerinden dönüşe gerek yoktur. HTTP yanıt gövdesi veya veritabanı sütunu gibi ham JSON baytlarınız olduğunda en hızlı seçenektir.

Go
import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
)

raw := []byte(`{"endpoint":"/api/v2/faturalar","page":1,"per_page":50,"total":312}`)

var buf bytes.Buffer
if err := json.Indent(&buf, raw, "", "	"); err != nil {
	log.Fatalf("indent: %v", err)
}
fmt.Println(buf.String())
// {
// 	"endpoint": "/api/v2/faturalar",
// 	"page": 1,
// 	"per_page": 50,
// 	"total": 312
// }

json.MarshalIndent neden hata döndürür?

encoding/json, değer JSON olarak temsil edilemediğinde hata döndürür. En yaygın nedenler: bir kanal, fonksiyon veya karmaşık sayı marshal etmek (bunların JSON karşılığı yoktur); MarshalJSON() uygulayan ve hata döndüren bir struct; veya string olmayan anahtarlara sahip bir map. Önemli olan: dışa aktarılmamış veya nil pointer alana sahip struct marshal etmek hata oluşturmaz — bunlar yalnızca atlanır.

Go
import (
	"encoding/json"
	"fmt"
)

// Bu hata döndürür — kanallar JSON'a dönüştürülemez
ch := make(chan int)
_, err := json.MarshalIndent(ch, "", "	")
fmt.Println(err)
// json: unsupported type: chan int

// Bu sorunsuz çalışır — omitempty ile nil pointer alanlar atlanır
type Profile struct {
	ID     string  `json:"id"`
	Avatar *string `json:"avatar,omitempty"`
}
p := Profile{ID: "usr_7b3c"}
data, _ := json.MarshalIndent(p, "", "  ")
// {"id": "usr_7b3c"}  — avatar atlandı, hata yok

Go'da bir alan JSON çıktısından nasıl hariç tutulur?

Üç yol vardır. Birincisi, json:"-" struct etiketini kullanmak — alan, değerinden bağımsız olarak her zaman hariç tutulur. İkincisi, omitempty kullanmak — alan yalnızca türünün sıfır değerini tuttuğunda hariç tutulur (nil pointer, boş string, 0, false). Üçüncüsü, dışa aktarılmamış (küçük harf) alanlar herhangi bir etiket gerekmeksizin encoding/json tarafından otomatik olarak hariç tutulur.

Go
type PaymentMethod struct {
	ID           string  `json:"id"`
	Last4        string  `json:"last4"`
	ExpiryMonth  int     `json:"expiry_month"`
	ExpiryYear   int     `json:"expiry_year"`
	CVV          string  `json:"-"`                    // her zaman hariç tutulur
	BillingName  string  `json:"billing_name,omitempty"` // boşsa hariç tutulur
	internalRef  string                                 // dışa aktarılmamış — otomatik hariç
}

pm := PaymentMethod{
	ID: "pm_9f3a", Last4: "4242",
	ExpiryMonth: 12, ExpiryYear: 2028,
	CVV: "123", internalRef: "stripe:pm_9f3a",
}
data, _ := json.MarshalIndent(pm, "", "  ")
// CVV, BillingName (boş) ve internalRef çıktıda görünmez

JSON marshal ederken time.Time nasıl işlenir?

encoding/json, time.Time değerlerini varsayılan olarak RFC3339Nano formatında marshal eder (ör. "2026-03-10T14:22:00Z"), bu ISO 8601 uyumludur. Eski bir API için Unix epoch integer veya özel tarih-sadece string gibi farklı bir format gerekiyorsa, time.Time gömülü bir wrapper tipe MarshalJSON() uygulayın.

Go
import (
	"encoding/json"
	"fmt"
	"time"
)

// Varsayılan davranış — RFC3339Nano, özel kod gerekmez
type AuditEvent struct {
	Action    string    `json:"action"`
	OccurredAt time.Time `json:"occurred_at"`
}

e := AuditEvent{
	Action:    "invoice.paid",
	OccurredAt: time.Date(2026, 3, 10, 14, 22, 0, 0, time.UTC),
}
data, _ := json.MarshalIndent(e, "", "  ")
// {
//   "action": "invoice.paid",
//   "occurred_at": "2026-03-10T14:22:00Z"
// }

// Özel: Unix timestamp integer olarak
type UnixTime struct{ time.Time }

func (u UnixTime) MarshalJSON() ([]byte, error) {
	return []byte(fmt.Sprintf("%d", u.Unix())), nil
}

İlgili Araçlar

Şu dillerde de mevcut:PythonJavaScriptBash
JO
James OkaforSystems Engineer

James is a systems engineer and Go enthusiast who focuses on high-performance microservices, command-line tooling, and infrastructure automation. He enjoys the simplicity and explicitness of Go and writes about building fast, reliable backend systems. When not coding he explores distributed systems concepts and contributes to open-source Go libraries.

TM
Tobias MüllerTeknik İnceleyici

Tobias is a platform engineer who builds developer tooling and internal infrastructure in Go. He has authored several open-source CLI tools and contributes to the Go toolchain ecosystem. He writes about the cobra and urfave/cli frameworks, cross-platform binary distribution, configuration management, and the patterns that make Go an ideal language for building reliable, self-contained command-line utilities.