-
Notifications
You must be signed in to change notification settings - Fork 0
/
csvtojson.go
112 lines (94 loc) · 2.23 KB
/
csvtojson.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
package main
import (
"encoding/csv"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"strings"
)
const flagItemDelimiter = ","
func main() {
var manualHeaders headerRow
flag.Var(&manualHeaders, "t", "Comma separated values representing the column titles (headers). Implies -n")
disableFirstRowHeader := flag.Bool("n", false, "Do not use the first row of each file as column names")
out := os.Stdout
flag.Parse()
args := flag.Args()
if len(args) == 0 {
err := processReader(os.Stdin, out, manualHeaders, !*disableFirstRowHeader)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "error processin stdin: %v", err)
os.Exit(1)
}
return
}
for _, r := range args {
err := processFile(r, out, manualHeaders, !*disableFirstRowHeader)
if err != nil {
_, _ = fmt.Fprintf(os.Stderr, "error processing file %s: %v", r, err)
os.Exit(1)
}
}
}
type headerRow []string
func (r *headerRow) String() string {
return strings.Join(*r, flagItemDelimiter)
}
func (r *headerRow) Set(value string) error {
if len(*r) > 0 {
return fmt.Errorf("error row already set")
}
*r = strings.Split(value, flagItemDelimiter)
return nil
}
func processFile(fn string, out io.Writer, manualHeaders headerRow, firstRowHeader bool) error {
f, err := os.Open(fn)
if err != nil {
return err
}
defer f.Close()
return processReader(f, out, manualHeaders, firstRowHeader)
}
func processReader(r io.Reader, out io.Writer, manualHeaders headerRow, firstRowHeader bool) error {
cr := csv.NewReader(r)
var header []string
var err error
if len(manualHeaders) > 0 {
header = manualHeaders
} else if firstRowHeader {
header, err = cr.Read()
if err != nil {
return err
}
for i := range header {
header[i] = strings.TrimSpace(header[i])
}
}
fieldName := func(idx int) string {
if len(header) <= idx {
return fmt.Sprintf("_column%v", idx)
}
return header[idx]
}
for {
row, err := cr.Read()
if err == io.EOF {
break
} else if err != nil {
return err
}
tmp := make(map[string]interface{})
for i, v := range row {
tmp[fieldName(i)] = v
}
js, err := json.Marshal(tmp)
if err != nil {
line, _ := cr.FieldPos(0)
return fmt.Errorf("line %v: %w", line, err)
}
_, _ = fmt.Fprintln(out, string(js))
}
return nil
}