我正在尝试包装一个io.ReaderCloser
,该在生产中将来自请求处理程序,带有可以传递到JSON解码器的自定义读取器。
我创建了以下内容
import (
"io"
)
// RemoveNull is a stream wrapper that should remove null bytes from the byte stream
type RemoveNull struct {
Reader io.ReadCloser
}
// NewRemoveNullStream creates a new RemoveNull reader which passes the stream through a null check first
func NewRemoveNullStream(reader io.ReadCloser) RemoveNull {
return RemoveNull{
Reader: reader,
}
}
// Read wraps a Reader to remove null bytes in the stream
func (null RemoveNull) Read(p []byte) (n int, err error) {
n, err = null.Reader.Read(p)
if err != nil {
return n, err
}
nn := 0
for i := range p {
if p[i] != 0 {
p[nn] = p[i]
nn++
}
}
p = p[:nn]
// fmt.Println(p) i can see the value of p changing and all the null bytes are removed
return n, nil
}
// Close closes the internal reader
func (null RemoveNull) Close() error {
return null.Close()
}
当我运行以下内容时,我可以从 print 语句中看到,确实删除了所有空字节,并且 len(p( == 所有预期良好字节的大小。我在下面编写了测试,看看代码是否按我的预期工作,这就是我意识到它不是的地方。
这是完整的测试
import (
"bytes"
"encoding/json"
"fmt"
"io"
"io/ioutil"
"testing"
"github.com/francoispqt/gojay" // can be replaced with the std json lib, code still doesn't work
)
func TestRemoveNull_Read(t *testing.T) {
type fields struct {
Reader io.ReadCloser
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "should remove null bytes",
fields: fields{
Reader: ioutil.NopCloser(bytes.NewReader([]byte{123, 34, 98, 111, 100, 121, 34, 58, 34, 102, 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 109, 97, 108, 101, 34, 125})),
},
want: "female",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
reader := tt.fields.Reader
reader = NewRemoveNullStream(tt.fields.Reader) // wrapper the reader above in the nullByte reader
// passed the reader into this JSON unmarshaller
decoder := gojay.BorrowDecoder(reader)
defer decoder.Release()
var v _testStruct
err := decoder.DecodeObject(&v)
if err != nil {
t.Fatalf("ReadAll failed %v", err)
}
bb, _ := json.Marshal(v)
fmt.Println(string(bb)) // all the null bytes are still present
fmt.Println(len(v.Body), len(tt.want))
if v.Body != tt.want {
t.Fatalf("DecodeObject() unexpected value, got %s want %s", v.Body, tt.want)
}
})
}
}
type _testStruct struct {
Body string `json:"body"`
}
func (v *_testStruct) UnmarshalJSONObject(dec *gojay.Decoder, k string) error {
switch k {
case "body":
err := dec.String(&v.Body)
return err
}
return nil
}
// NKeys returns the number of keys to unmarshal
func (v *_testStruct) NKeys() int { return 0 }
从测试中,我可以看到解码时所有空字节仍然存在,但在 RemoveNull 阅读器中,我可以看到所有空字节都已从下划线数组中删除。关于出了什么问题以及如何实现从流中删除字节以避免解码器解码空字节的目标的任何想法?
Read 实现中存在错误。在io的情况下,它会提前终止。EOF,其中既有错误又有数据。它返回读取的错误字节数。分配切片的最后一部分也毫无意义,因为它不会更新传递给函数的切片。
试试这个:
func (null RemoveNull) Read(p []byte) (n int, err error) {
n, err = null.Reader.Read(p)
nn := 0
for i:=0;i<n;i++ {
if p[i] != 0 {
p[nn] = p[i]
nn++
}
}
return nn, err
}