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

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

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&#39;t want it all to be in memory at the same time.
	Bar chan string 
}{
	Foo: &quot;Hello World&quot;,
	Bar: make(chan string),
}

// long stream of data
go func() {
	for _, x := range []string{&quot;one&quot;, &quot;two&quot;, &quot;three&quot;} {
		t.Bar &lt;- x
	}
	close(t.Bar)
}()

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

playground

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

I'm currently just building the json string myself.

playground

w := os.Stdout
w.WriteString(`{ &quot;Foo&quot;: &quot;` + t.Foo + `&quot;, &quot;Bar&quot;: [`)

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(&#39;[&#39;)
    n := v.Len()
    for i := 0; i &lt; n; i++ {
        if i &gt; 0 {
            e.WriteByte(&#39;,&#39;)
        }
        e.reflectValue(v.Index(i))
    }
    e.WriteByte(&#39;]&#39;)

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(&#39;[&#39;)
    i := 0
    for {
        x, ok := v.Recv()
        if !ok {
            break
        }
        if i &gt; 0 {
            e.WriteByte(&#39;,&#39;)
        }
        e.reflectValue(x)
        i++
    }
    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方法中解包通道,像这样:

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
}

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:

确定