将os.Create()的打开文件描述符传递给接受类型io.Reader的函数并运行io.Copy(b.Bytes(),reader)后,有效JSON的json.Unmarshal失败。

以下代码块中的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()方法中,我们尝试在2种情况下读取/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
    }
    

    第二,如果文件不为空,我们将数据写入文件,然后立即将其读回并解组,以满足后来的功能,该功能在比较的数据和写入文件的数据之间执行reflect.DeepEqual

    在这两种情况下,Read()方法都返回“JSON输入的意外结尾”,并在文件/var/log/collected_data.json中带有有效的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”:“错误”,“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
     }
    

    关于go - io.Reader可以接受文件描述符吗? “JSON输入意外结束”,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/57329333/

    10-14 12:57
    查看更多