douji8347 2019-08-02 15:17
浏览 44
已采纳

io.Reader可以接受文件描述符吗? “ JSON输入意外结束”

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?

  1. Verified that the JSON is fully valid. Validated with jq and 3 online JSON validators.
  2. Verified that data is actually being written to the file successfully through another function that implements io.Write.
  3. 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())
    }
  • 写回答

1条回答 默认 最新

  • douwu7168 2019-08-02 15:48
    关注

    The answer to the question is yes. An *os.File can be used as an io.Reader.

    The problem is that the application writes data to the file and then attempts to read that same data from the file without seeking back where the data was written.

    Fix the problem by adding the following code after the call to store.Write and before the call to store.Read.

     if _, err := s.DataStore.Seek(io.SeekStart, 0); err != nil {
         return err
     }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 ubuntu子系统密码忘记
  • ¥15 信号傅里叶变换在matlab上遇到的小问题请求帮助
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料
  • ¥15 使用R语言marginaleffects包进行边际效应图绘制
  • ¥20 usb设备兼容性问题
  • ¥15 错误(10048): “调用exui内部功能”库命令的参数“参数4”不能接受空数据。怎么解决啊
  • ¥15 安装svn网络有问题怎么办
  • ¥15 vue2登录调用后端接口如何实现