-
Notifications
You must be signed in to change notification settings - Fork 0
/
json.go
233 lines (180 loc) · 5.33 KB
/
json.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
package jsonfileworker
import (
"fmt"
"io"
"os"
"path/filepath"
jsoniter "github.com/json-iterator/go"
)
/*
Add the following function:
UpdateObject(pathToFile string, v interface{}, conditions, whatToUpdate)
The conditions can be the following:
- Index of element to be updated
- Some equals, greaterthan, lessthan conditions (could use an enum for this)
Can have multiple functions like:
UpdateObjectThroughIndex
UpdateObjectThroughCondition
- name == "Ervin Howell" => username = "EH"
since they would have different signatures and implementations I feel
*/
/*
Additional info:
If the user passes a certain type in the generics and the object returned does not have certain fields, then they would be replaced with their zero values, unless the json is specified to omitempty. If the user doesnt want to conform to a particular type,they are advised to use any as the the type
*/
var (
// This is taken from the jsoniter library docs.
// It has the configs for the jsoniter.ConfigCompatibleWithStandardLibrary API + DisallowUnknowFields set to true, so as to have typesafety in case the JSON is of different type than the type given
json = jsoniter.Config{
EscapeHTML: true,
SortMapKeys: true,
ValidateJsonRawMessage: true,
DisallowUnknownFields: true,
}.Froze()
)
func GetAllObjects(pathToFile string, obj interface{}) error {
absPathToFile, _ := filepath.Abs(pathToFile)
pathFile, err := os.Open(absPathToFile)
if err != nil {
return err
}
defer pathFile.Close()
byteValue, err := io.ReadAll(pathFile)
if err != nil {
return err
}
err = json.Unmarshal(byteValue, obj)
if err != nil {
return err
}
return nil
}
/*
Returns the individual object at the given index.
Takes in the type that should be returned (This would help in typesafety as well as autocomplete)
Returns error when:
1. The file is not found
2. There is some error reading the file contents
3. The actual JSON content is not an array (need it to be an array to have an index for this one)
4. The content received is not of the type passed in
* Can pass in any as the type in the function if type is not known
* The function works with optional field values as well
*/
func GetObjectFromIndex[T any](pathToFile string, index int) (T, error) {
var zeroVal T // This is the zero value for the datatype
absPathToFile, _ := filepath.Abs(pathToFile)
pathFile, err := os.Open(absPathToFile)
if err != nil {
return zeroVal, err
}
defer pathFile.Close()
byteValue, err := io.ReadAll(pathFile)
if err != nil {
return zeroVal, err
}
var newObj []T
err = json.Unmarshal(byteValue, &newObj)
if err != nil {
return zeroVal, err
}
if index < 0 && len(newObj) <= index {
return zeroVal, fmt.Errorf("index %d not present in array", index)
}
return newObj[index], nil
}
// Will see if I can work on updating modularly if possible
func SetAllObjects(pathToFile string, obj interface{}) error {
absPathToFile, _ := filepath.Abs(pathToFile)
pathFile, err := os.OpenFile(absPathToFile, os.O_WRONLY|os.O_TRUNC, 0755)
if err != nil {
return err
}
defer pathFile.Close()
updatedContents, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
_, err = pathFile.Write(updatedContents)
if err != nil {
return err
}
return nil
}
// Appends entire object to the array in file
// Does not yet work on arrays in individual fields
// This function follows the Read-Modify-Write approach
func AppendObjectToArray[T any](pathToFile string, obj T) error {
absPathToFile, _ := filepath.Abs(pathToFile)
pathFile, err := os.OpenFile(absPathToFile, os.O_RDWR, os.ModePerm)
if err != nil {
return err
}
defer pathFile.Close()
byteValue, err := io.ReadAll(pathFile)
if err != nil {
return err
}
var objects []T
err = json.Unmarshal(byteValue, &objects)
if err != nil {
return err
}
objects = append(objects, obj)
updatedContents, err := json.MarshalIndent(objects, "", " ")
if err != nil {
return err
}
if err = pathFile.Truncate(0); err != nil {
return err
}
if _, err := pathFile.Seek(0, 0); err != nil {
return err
}
_, err = pathFile.Write(updatedContents)
if err != nil {
return err
}
return nil
}
// AppendObjectToArrayDirect - This will not read the entire file contents to memory, but instead append it directly using writeStrings ig
// ! Need to fix the indenting issues. Seeking -2 works in some cases, but not during empty file & only brackets situation.
func AppendObjectToArrayDirect[T any](pathToFile string, obj T) error {
absPathToFile, _ := filepath.Abs(pathToFile)
pathFile, err := os.OpenFile(absPathToFile, os.O_RDWR, os.ModePerm)
if err != nil {
return err
}
defer pathFile.Close()
stat, err := pathFile.Stat()
if err != nil {
return err
}
// This is to initialize an array if the file is empty
if stat.Size() == 0 {
_, err = pathFile.WriteString("[\n")
if err != nil {
return err
}
}
if _, err = pathFile.Seek(-1, io.SeekEnd); err != nil {
return err
}
// Adds a comma after the objects already present
if stat.Size() > 2 {
if _, err = pathFile.WriteString(",\n"); err != nil {
return err
}
}
newObject, err := json.MarshalIndent(obj, "", " ")
if err != nil {
return err
}
if _, err = pathFile.Write(newObject); err != nil {
return err
}
// Closing the array
if _, err = pathFile.WriteString("\n]"); err != nil {
return err
}
return nil
}