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 微信小程序协议怎么写
  • ¥15 c语言怎么用printf(“\b \b”)与getch()实现黑框里写入与删除?
  • ¥20 怎么用dlib库的算法识别小麦病虫害
  • ¥15 华为ensp模拟器中S5700交换机在配置过程中老是反复重启
  • ¥15 java写代码遇到问题,求帮助
  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看