json.Unmarshal
of valid JSON fails, after passing open file descriptor from os.Create() to a function the accepts type io.Reader and running io.Copy(b.Bytes(), reader)
Is the Read()
method in the below code blocks being implemented correctly? io.Reader
wraps the Read
method, but will passing an open file descriptor to it read the file in to bytes, allowing io.Copy(b.Bytes(), reader) to copy the data into var b?
Is there a better way to do this without using ioutil.ReadAll
?
- Verified that the JSON is fully valid. Validated with jq and 3 online JSON validators.
- Verified that data is actually being written to the file successfully through another function that implements io.Write.
- Verified that
ioutil.ReadAll
can read the JSON in to bytes, so I think the method being used is implemented incorrectly.
** This isn't my code, I'm troubleshooting code written by someone else, who is unavailable for any questions **
I have a json file collected_data.json
.
The file is created with os.Create
file, err := os.Create("/var/log/collected_data.json")
A struct is configured, which sets file
to DataStore
:
profiler := &collectors.SystemProfiler{
DataStore: file,
NetworkInterfaces: interfaces,
ApiURL: *apiURL,
RunOnce: *runOnce,
SysTag: *sysTag,
}
We then run a Gather()
method.
err = profiler.Send(output)
if err != nil {
log.Error(err)
}
The Send()
method implements the SystemProfiler struct:
func (s *SystemProfiler) Send(profile *SystemProfile) error {...}
Everything works fine getting to this point, up until a section of code where we try to read and unmarshall data from /var/log/collected_data.json
.
Inside this Send()
method, we try to read the file /var/log/collected_data.json
in 2 scenarios.
1st, if the file is not empty, we read the file, and do something with it (not shown here).
data, err := store.Read(s.DataStore)
if err != nil {
log.Print("I couldn't read the datastore")
return err
}
2nd, if the file isn't empty, we write data to the file, and then immediately read it back in and Unmarshal to satisfy a later function that does a reflect.DeepEqual
between data compared and data written to the file.
In both cases, the Read()
method returns "Unexpected end of JSON input", with valid JSON in the file /var/log/collected_data.json
. The method being used to write data works just fine.
{"level":"info","msg":"I couldn't read the datastore","time":"2019-08-02T02:26:42-04:00"}
{"level":"error","msg":"unexpected end of JSON input","time":"2019-08-02T02:26:42-04:00"}
The Read()
method looks like this:
// Read reads JSON data from an io.Reader
func Read(reader io.Reader) (interface{}, error) {
var data interface{}
var b bytes.Buffer
io.Copy(&b, reader)
err := json.Unmarshal(b.Bytes(), &data)
if err != nil {
return nil, err
}
return data, nil
}
Expected results:
Valid JSON is copied from reader of type io.Reader to b of type bytes.Buffer, successfully unmarshalled, and returned.
Actual result: {"level":"error","msg":"unexpected end of JSON input","time":"2019-08-02T02:26:42-04:00"}
To answer a question asked in the comments, here is the block of code inside the Send()
function:
var storedProfile SystemProfile
check := store.IsEmpty(s.DataStore)
if check == false {
log.Print("Data Store Exists")
// If there is data stored, read it
data, err := store.Read(s.DataStore)
if err != nil {
return err
}
//var tmp SystemProfile
err = mapstructure.Decode(data, &storedProfile)
if err != nil {
return err
}
} else {
log.Print("Data Store Doesn't Exist")
// If the data store is empty, write to it
err := store.Write(s.DataStore, profile)
if err != nil {
return err
}
data, err := store.Read(s.DataStore)
if err != nil {
log.Print("I couldn't read the datastore")
return err
}
err = mapstructure.Decode(data, &storedProfile)
if err != nil {
log.Print("I couldn't decode the json to a map")
return err
}
body, err := json.Marshal(profile)
if err != nil {
return err
}
_, err = client.Post(s.ApiURL, "application/json", bytes.NewBuffer(body)) // TODO: Handle response from API here
log.Print(client.LogString())
}
if !reflect.DeepEqual(storedProfile, profile ) {
// If what's in the data store and what has been collected are different,
// write the recently collected data to the data store and send it out to the API
store.Write(s.DataStore, profile)
body, err := json.Marshal(profile)
if err != nil {
return err
}
_, err = client.Post(s.ApiURL, "application/json", bytes.NewBuffer(body)) // TODO: Handle response from API here
if err != nil {
return err
}
log.Print(client.LogString())
}