-
Notifications
You must be signed in to change notification settings - Fork 0
/
csvson.go
160 lines (145 loc) · 4.07 KB
/
csvson.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
package csvson
import (
"encoding/csv"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"os"
"strconv"
"strings"
)
func CSVToJSON(path string) {
data, err := CSV2Slice(path)
if err != nil {
panic(fmt.Sprintf("error while handling csv file: %s\n", err))
}
res, _ := JSONString(data)
WriteToJSON(res)
}
func JSONString(rows [][]string) (string, error) {
// Split headers and values
headerAttributes := rows[0]
values := rows[1:]
// Map of attributes and values
entities := CSVRowsToMap(headerAttributes, values)
// String of json formatted file
json, err := MapToString(entities)
if err != nil {
log.Fatalf("Converting map of entities to json failed due to the error: %s", err)
}
return json, err
}
// Recives path of CSV file as input and returns a sliced array of rows
func CSV2Slice(CSVPath string) ([][]string, error) {
inputFile := OpenCSV(CSVPath)
slicedRows := ReadCSV(inputFile)
return slicedRows, nil
}
// Opens the given path and returns the CSV file
func OpenCSV(path string) (CSVFile *os.File) {
file, err := os.Open(path)
if err != nil {
log.Fatalf("Error while opening the path: %s\n %s", path, err)
return nil
}
return file
}
// Reads the CSVFile and returns A 2D string slice of rows
func ReadCSV(CSVFile *os.File) (CSVRows [][]string) {
var csvRows [][]string
reader := csv.NewReader(CSVFile)
for {
// Read rows step by step
row, err := reader.Read()
// Stop reading when reached end of the file
if err == io.EOF {
break
}
if err != nil {
log.Fatalf("Somthing went wrong while parsing the CSV file,\n Error: %s", err)
return csvRows
}
csvRows = append(csvRows, row)
}
return csvRows
}
// Creates a map of CSV headers and values
func CSVRowsToMap(header []string, values [][]string) []map[string]interface{} {
var entities []map[string]interface{}
for _, row := range values {
entry := map[string]interface{}{}
for i, value := range row {
headerAttribute := header[i]
// Split CSV header attributes for nested attributes
attributeSlice := strings.Split(headerAttribute, ".")
internal := entry
for index, val := range attributeSlice {
// split csv header attributes to keys
key, attributeIndex := AttributesIndex(val)
if attributeIndex != -1 {
if internal[key] == nil {
internal[key] = []interface{}{}
}
internalAttributeArray := internal[key].([]interface{})
if index == len(attributeSlice)-1 {
internalAttributeArray = append(internalAttributeArray, value)
internal[key] = internalAttributeArray
break
}
if attributeIndex >= len(internalAttributeArray) {
internalAttributeArray = append(internalAttributeArray, map[string]interface{}{})
}
internal[key] = internalAttributeArray
internal = internalAttributeArray[attributeIndex].(map[string]interface{})
} else {
if index == len(attributeSlice)-1 {
internal[key] = value
break
}
if internal[key] == nil {
internal[key] = map[string]interface{}{}
}
internal = internal[key].(map[string]interface{})
}
}
}
entities = append(entities, entry)
}
return entities
}
// Recives map of entities and returns json formated result
func MapToString(entries []map[string]interface{}) (string, error) {
//MarshalIndent for json formating output
bytes, err := json.MarshalIndent(entries, "", " ")
if err != nil {
fmt.Printf("Something went wrong while marshalling byte slice, %s\n", err)
return "", errors.New(err.Error())
}
return string(bytes), nil
}
// Finds attribute and index of nested attributes and returns them
func AttributesIndex(attribute string) (string, int) {
i := strings.Index(attribute, "[")
if i >= 0 {
j := strings.Index(attribute, "]")
if j >= 0 {
index, _ := strconv.Atoi(attribute[i+1 : j])
return attribute[0:i], index
}
}
return attribute, -1
}
// Creates json file
func WriteToJSON(json string) {
// 0777 for read, write, & execute for owner, group and others
err := os.WriteFile("./result.json", []byte(json), 0777)
CheckError(err)
}
// Checking errors
func CheckError(err error) {
if err != nil {
panic(err)
}
}