go - io.Reader 可以接受文件描述符吗?“JSON 输入意外结束”
问题描述
json.Unmarshal
在将打开的文件描述符从 os.Create() 传递给接受类型 io.Reader 并运行 io.Copy(b.Bytes(), reader) 的函数后,有效 JSON 的失败
以下代码块中的Read()
方法是否正确实现?io.Reader
包装该Read
方法,但会将打开的文件描述符传递给它将文件读取为字节,从而允许 io.Copy(b.Bytes(), reader) 将数据复制到 var b?
有没有更好的方法可以在不使用的情况下做到这一点ioutil.ReadAll
?
- 已验证 JSON 完全有效。使用 jq 和 3 个在线 JSON 验证器进行验证。
- 通过另一个实现 io.Write 的函数验证数据实际上正在成功写入文件。
- 已验证
ioutil.ReadAll
可以将 JSON 读取为字节,因此我认为正在使用的方法实现不正确。
** 这不是我的代码,我正在对其他人编写的代码进行故障排除,无法回答任何问题 **
我有一个 json 文件collected_data.json
。
该文件是用 os.Create 创建的
file, err := os.Create("/var/log/collected_data.json")
配置了一个结构,它设置file
为DataStore
:
profiler := &collectors.SystemProfiler{
DataStore: file,
NetworkInterfaces: interfaces,
ApiURL: *apiURL,
RunOnce: *runOnce,
SysTag: *sysTag,
}
然后我们运行一个Gather()
方法。
err = profiler.Send(output)
if err != nil {
log.Error(err)
}
该Send()
方法实现了 SystemProfiler 结构:
func (s *SystemProfiler) Send(profile *SystemProfile) error {...}
到目前为止一切正常,直到我们尝试从/var/log/collected_data.json
.
在这个Send()
方法中,我们尝试/var/log/collected_data.json
在两种情况下读取文件。
1,如果文件不为空,我们读取文件,并用它做一些事情(这里没有显示)。
data, err := store.Read(s.DataStore)
if err != nil {
log.Print("I couldn't read the datastore")
return err
}
2,如果文件不为空,我们将数据写入文件,然后立即将其读回并解组,以满足稍后reflect.DeepEqual
在数据比较和写入文件的数据之间进行的函数。
在这两种情况下,该Read()
方法都返回“JSON 输入意外结束”,文件中包含有效的 JSON /var/log/collected_data.json
。用于写入数据的方法工作得很好。
{"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"}
该Read()
方法如下所示:
// 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
}
预期成绩:
有效的 JSON 从 io.Reader 类型的读取器复制到 bytes.Buffer 类型的 b,成功解组并返回。
实际结果:{"level":"error","msg":"JSON 输入意外结束","time":"2019-08-02T02:26:42-04:00"}
要回答评论中提出的问题,这里是Send()
函数内的代码块:
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())
}
解决方案
这个问题的答案是肯定的。可以*os.File
用作io.Reader
.
问题是应用程序将数据写入文件,然后尝试从文件中读取相同的数据,而不回溯数据的写入位置。
通过在调用 store.Write 和调用 store.Read 之前添加以下代码来解决此问题。
if _, err := s.DataStore.Seek(io.SeekStart, 0); err != nil {
return err
}
推荐阅读
- asp.net - “模型项传入字典”错误,但传入和必需的是相同的模型
- excel - Excel GETPIVOTDATA 公式 - 如何拖动公式并自动更改日期?
- mysql - 如何使用 MySQL Group Concat
- apache-spark - pySpark.sql 如何使用 WHERE 关键字?
- anaconda - Jupyter Notebook 未连接到 python 内核
- excel - 用于将 Chartsheets 打印到 PDF 的 VBA 宏以奇怪的比例生成截止图表
- google-apps-script - 我有一段代码有效,但效率很低。它可能可以改进,但我不明白“return”是如何工作的
- php - SOAP 响应。如何处理?
- php - 如何使用会话变量实现访问控制系统?
- javascript - 重新排列音频节点时是否保证无缝播放?