Stream a JSON Array with an Encoder

Encode a large result set incrementally with json.Encoder and a buffered writer instead of marshalling the whole slice at once.

GoData Fetching

Code

snippet
package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"strings"
	"time"
)

type Row struct {
	ID      int       `json:"id"`
	Message string    `json:"message"`
	At      time.Time `json:"at"`
}

func StreamJSONArray(w io.Writer, rows <-chan Row) error {
	buffered := bufio.NewWriter(w)
	encoder := json.NewEncoder(buffered)
	encoder.SetEscapeHTML(false)

	if _, err := buffered.WriteString("["); err != nil {
		return err
	}
	first := true
	for row := range rows {
		if !first {
			if _, err := buffered.WriteString(","); err != nil {
				return err
			}
		}
		if err := encoder.Encode(row); err != nil {
			return err
		}
		first = false
	}
	if _, err := buffered.WriteString("]"); err != nil {
		return err
	}
	return buffered.Flush()
}

func main() {
	rows := make(chan Row, 3)
	at := time.Date(2026, 10, 21, 9, 0, 0, 0, time.UTC)
	for i := 1; i <= 3; i++ {
		rows <- Row{ID: i, Message: fmt.Sprintf("row %d", i), At: at.Add(time.Duration(i) * time.Minute)}
	}
	close(rows)

	var out strings.Builder
	if err := StreamJSONArray(&out, rows); err != nil {
		fmt.Println("encode:", err)
		return
	}
	fmt.Println(out.String())
}