英文:
MarshalJSON without having all objects in memory at once
问题
我想使用json.Encoder
来编码一个大量的数据流,而不需要一次性将所有数据加载到内存中。
// 我想要编组这个结构体
t := struct {
Foo string
// Bar 是一个对象流
// 我不希望它一次性全部加载到内存中。
Bar chan string
}{
Foo: "Hello World",
Bar: make(chan string),
}
// 长数据流
go func() {
for _, x := range []string{"one", "two", "three"} {
t.Bar <- x
}
close(t.Bar)
}()
我以为`json`包中可能有这个功能,但事实并非如此。
[playground](http://play.golang.org/p/3uwTdXqv3v)
// 错误:json: unsupported type: chan string
if err := json.NewEncoder(os.Stdout).Encode(&t); err != nil {
log.Fatal(err)
}
我目前只是自己构建json字符串。
[playground](http://play.golang.org/p/570DuHHE7i)
w := os.Stdout
w.WriteString(`{ "Foo": "` + t.Foo + `", "Bar": [`)
for x := range t.Bar {
_ = json.NewEncoder(w).Encode(x)
w.WriteString(`,`)
}
w.WriteString(`]}`)
有更好的方法吗?
如果`json.Marshaler`像这样,就会很简单。
type Marshaler interface {
MarshalJSON(io.Writer) error
}
英文:
I want to use json.Encoder
to encode a large stream of data without loading all of it into memory at once.
// I want to marshal this
t := struct {
Foo string
// Bar is a stream of objects
// I don't want it all to be in memory at the same time.
Bar chan string
}{
Foo: "Hello World",
Bar: make(chan string),
}
// long stream of data
go func() {
for _, x := range []string{"one", "two", "three"} {
t.Bar <- x
}
close(t.Bar)
}()
I thought maybe the json package had this functionality build in, but that's not the case.
// error: json: unsupported type: chan string
if err := json.NewEncoder(os.Stdout).Encode(&t); err != nil {
log.Fatal(err)
}
I'm currently just building the json string myself.
w := os.Stdout
w.WriteString(`{ "Foo": "` + t.Foo + `", "Bar": [`)
for x := range t.Bar {
_ = json.NewEncoder(w).Encode(x)
w.WriteString(`,`)
}
w.WriteString(`]}`)
Is there a better way to do this?
If the json.Marshaler was like this is would be trivial.
type Marshaler interface {
MarshalJSON(io.Writer) error
}
答案1
得分: 2
很遗憾,encoding/json
包目前还没有这样的方法。你现在所做的(手动处理)是在不修改内置包的情况下最好的方法。
如果你要修补encoding/json
,你可以修改encoding/json/encode.go中的reflectValueQuoted
函数。
你需要关注数组的情况(切片使用fallthrough
):
// 在switch语句中:
case reflect.Array:
e.WriteByte('[')
n := v.Len()
for i := 0; i < n; i++ {
if i > 0 {
e.WriteByte(',')
}
e.reflectValue(v.Index(i))
}
e.WriteByte(']')
我假设你希望以相同的方式处理通道。代码可能如下所示:
// 在switch语句中:
case reflect.Chan:
e.WriteByte('[')
i := 0
for {
x, ok := v.Recv()
if !ok {
break
}
if i > 0 {
e.WriteByte(',')
}
e.reflectValue(x)
i++
}
e.WriteByte(']')
我在reflect
中对通道的处理不多,所以上述代码可能需要其他检查。
如果你最终选择这个方法,你可以提交一个补丁。
英文:
Unfortunately the encoding/json
package doesn't have a way to do this yet. What you're doing now (manually) is the best way to do it, without modifying the built-in package.
If you were to patch encoding/json
, you could modify the reflectValueQuoted
function in encoding/json/encode.go
You would want to focus on the Array case (Slice has a fallthrough
):
// Inside switch:
case reflect.Array:
e.WriteByte('[')
n := v.Len()
for i := 0; i < n; i++ {
if i > 0 {
e.WriteByte(',')
}
e.reflectValue(v.Index(i))
}
e.WriteByte(']')
I'm assuming you'd want to treat channel the same way. It would look something like this:
// Inside switch:
case reflect.Chan:
e.WriteByte('[')
i := 0
for {
x, ok := v.Recv()
if !ok {
break
}
if i > 0 {
e.WriteByte(',')
}
e.reflectValue(x)
i++
}
e.WriteByte(']')
I haven't done much with channels in reflect
, so the above may need other checks.
If you do end up going this route, you could always submit a patch.
答案2
得分: 1
你可以在结构体的MarshalJSON
方法中解包通道,像这样:
type S struct {
Foo string
Bar chan string
}
func (s *S) MarshalJSON() (b []byte, err error) {
b, err := json.Marshal(s.Foo)
if err != nil { return nil, err }
for x := range s.Bar {
tmp, err := json.Marshal(x)
if err != nil { return nil, err }
b = append(b, tmp...)
}
return
}
这段代码将会把通道中的数据进行解包,并将其转换为JSON格式。
英文:
You can unpack the channel in the MarshalJSON
method in your struct like this:
type S struct {
Foo string
Bar chan string
}
func (s *S) MarshalJSON() (b []byte, err error) {
b, err := json.Marshal(s.Foo)
if err != nil { return nil, err }
for x := range s.Bar {
tmp, err := json.Marshal(x)
if err != nil { return nil, err }
b = append(b, tmp...)
}
return
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论