package main import ( "encoding/json" "fmt" "slices" "strings" ) type Document struct { Header []string Rows [][]string } func (d Document) MarshalJSON() ([]byte, error) { headers := keys(d.Header) var ret strings.Builder ret.WriteRune('[') for i, row := range d.Rows { ret.WriteRune('{') for j, col := range row { obj, err := json.Marshal(map[string]string{headers[j]: col}) if err != nil { return nil, fmt.Errorf("failed to marshal %s: %w", headers[j], err) } ret.WriteString(strings.Trim(string(obj), "{}")) if j < len(row)-1 { ret.WriteRune(',') } } if i < len(d.Rows)-1 { ret.WriteString("},") } else { ret.WriteRune('}') } } ret.WriteRune(']') return []byte(ret.String()), nil } func (d *Document) setHeader(h []string) error { d.Header = h return nil } func (d *Document) addRow(row []string) error { d.Rows = append(d.Rows, row) return nil } func keys(list []string) []string { keys := make([]string, 0, len(list)) for _, h := range list { heading := h index := 0 for slices.Contains(keys, heading) { index++ heading = fmt.Sprintf("%s_%d", h, index) } keys = append(keys, heading) } return keys }