MarshalJSON在不同时将所有对象一次性加载到内存中的情况下进行翻译。

huangapple go评论121阅读模式
英文:

MarshalJSON without having all objects in memory at once

问题

我想使用json.Encoder来编码一个大量的数据流,而不需要一次性将所有数据加载到内存中。

  1. // 我想要编组这个结构体
  2. t := struct {
  3. Foo string
  4. // Bar 是一个对象流
  5. // 我不希望它一次性全部加载到内存中。
  6. Bar chan string
  7. }{
  8. Foo: "Hello World",
  9. Bar: make(chan string),
  10. }
  11. // 长数据流
  12. go func() {
  13. for _, x := range []string{"one", "two", "three"} {
  14. t.Bar <- x
  15. }
  16. close(t.Bar)
  17. }()
  18. 我以为`json`包中可能有这个功能但事实并非如此
  19. [playground](http://play.golang.org/p/3uwTdXqv3v)
  20. // 错误:json: unsupported type: chan string
  21. if err := json.NewEncoder(os.Stdout).Encode(&t); err != nil {
  22. log.Fatal(err)
  23. }
  24. 我目前只是自己构建json字符串
  25. [playground](http://play.golang.org/p/570DuHHE7i)
  26. w := os.Stdout
  27. w.WriteString(`{ "Foo": "` + t.Foo + `", "Bar": [`)
  28. for x := range t.Bar {
  29. _ = json.NewEncoder(w).Encode(x)
  30. w.WriteString(`,`)
  31. }
  32. w.WriteString(`]}`)
  33. 有更好的方法吗
  34. 如果`json.Marshaler`像这样就会很简单
  35. type Marshaler interface {
  36. MarshalJSON(io.Writer) error
  37. }
英文:

I want to use json.Encoder to encode a large stream of data without loading all of it into memory at once.

  1. // I want to marshal this
  2. t := struct {
  3. Foo string
  4. // Bar is a stream of objects
  5. // I don&#39;t want it all to be in memory at the same time.
  6. Bar chan string
  7. }{
  8. Foo: &quot;Hello World&quot;,
  9. Bar: make(chan string),
  10. }
  11. // long stream of data
  12. go func() {
  13. for _, x := range []string{&quot;one&quot;, &quot;two&quot;, &quot;three&quot;} {
  14. t.Bar &lt;- x
  15. }
  16. close(t.Bar)
  17. }()

I thought maybe the json package had this functionality build in, but that's not the case.

playground

  1. // error: json: unsupported type: chan string
  2. if err := json.NewEncoder(os.Stdout).Encode(&amp;t); err != nil {
  3. log.Fatal(err)
  4. }

I'm currently just building the json string myself.

playground

  1. w := os.Stdout
  2. w.WriteString(`{ &quot;Foo&quot;: &quot;` + t.Foo + `&quot;, &quot;Bar&quot;: [`)
  3. for x := range t.Bar {
  4. _ = json.NewEncoder(w).Encode(x)
  5. w.WriteString(`,`)
  6. }
  7. w.WriteString(`]}`)

Is there a better way to do this?

If the json.Marshaler was like this is would be trivial.

  1. type Marshaler interface {
  2. MarshalJSON(io.Writer) error
  3. }

答案1

得分: 2

很遗憾,encoding/json包目前还没有这样的方法。你现在所做的(手动处理)是在不修改内置包的情况下最好的方法。

如果你要修补encoding/json,你可以修改encoding/json/encode.go中的reflectValueQuoted函数。

你需要关注数组的情况(切片使用fallthrough):

  1. // 在switch语句中:
  2. case reflect.Array:
  3. e.WriteByte('[')
  4. n := v.Len()
  5. for i := 0; i < n; i++ {
  6. if i > 0 {
  7. e.WriteByte(',')
  8. }
  9. e.reflectValue(v.Index(i))
  10. }
  11. e.WriteByte(']')

我假设你希望以相同的方式处理通道。代码可能如下所示:

  1. // 在switch语句中:
  2. case reflect.Chan:
  3. e.WriteByte('[')
  4. i := 0
  5. for {
  6. x, ok := v.Recv()
  7. if !ok {
  8. break
  9. }
  10. if i > 0 {
  11. e.WriteByte(',')
  12. }
  13. e.reflectValue(x)
  14. i++
  15. }
  16. 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):

  1. // Inside switch:
  2. case reflect.Array:
  3. e.WriteByte(&#39;[&#39;)
  4. n := v.Len()
  5. for i := 0; i &lt; n; i++ {
  6. if i &gt; 0 {
  7. e.WriteByte(&#39;,&#39;)
  8. }
  9. e.reflectValue(v.Index(i))
  10. }
  11. e.WriteByte(&#39;]&#39;)

I'm assuming you'd want to treat channel the same way. It would look something like this:

  1. // Inside switch:
  2. case reflect.Chan:
  3. e.WriteByte(&#39;[&#39;)
  4. i := 0
  5. for {
  6. x, ok := v.Recv()
  7. if !ok {
  8. break
  9. }
  10. if i &gt; 0 {
  11. e.WriteByte(&#39;,&#39;)
  12. }
  13. e.reflectValue(x)
  14. i++
  15. }
  16. e.WriteByte(&#39;]&#39;)

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方法中解包通道,像这样:

  1. type S struct {
  2. Foo string
  3. Bar chan string
  4. }
  5. func (s *S) MarshalJSON() (b []byte, err error) {
  6. b, err := json.Marshal(s.Foo)
  7. if err != nil { return nil, err }
  8. for x := range s.Bar {
  9. tmp, err := json.Marshal(x)
  10. if err != nil { return nil, err }
  11. b = append(b, tmp...)
  12. }
  13. return
  14. }

这段代码将会把通道中的数据进行解包,并将其转换为JSON格式。

英文:

You can unpack the channel in the MarshalJSON method in your struct like this:

  1. type S struct {
  2. Foo string
  3. Bar chan string
  4. }
  5. func (s *S) MarshalJSON() (b []byte, err error) {
  6. b, err := json.Marshal(s.Foo)
  7. if err != nil { return nil, err }
  8. for x := range s.Bar {
  9. tmp, err := json.Marshal(x)
  10. if err != nil { return nil, err }
  11. b = append(b, tmp...)
  12. }
  13. return
  14. }

huangapple
  • 本文由 发表于 2013年8月9日 02:58:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/18133950.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定