【问题标题】:MarshalJSON without having all objects in memory at onceMarshalJSON 没有一次将所有对象都放在内存中
【发布时间】:2013-08-08 18:58:34
【问题描述】:

我想使用json.Encoder 对大量数据进行编码,而不是一次将所有数据加载到内存中。

// 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)
}()

我想也许 json 包内置了这个功能,但事实并非如此。

playground

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

我目前只是自己构建 json 字符串。

playground

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
}

【问题讨论】:

    标签: go marshalling


    【解决方案1】:

    不幸的是,encoding/json 包还没有办法做到这一点。您现在(手动)执行的操作是最好的方法,无需修改内置包。

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

    您可能希望专注于 Array 案例(Slice 有一个 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(']')
    

    我假设您想以同样的方式对待频道。它看起来像这样:

    // 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(']')
    

    reflect 的频道我没做太多,所以上面可能需要其他检查。

    如果你最终走这条路,你可以随时提交补丁。

    【讨论】:

    • @iliacholy,如果你打算做那个补丁,请告诉我。这听起来很有趣,如果你不打算这样做,我想试一试。
    【解决方案2】:

    您可以像这样在结构中的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
    }
    

    【讨论】:

    • 该方法一次将所有内容加载到内存中。这正是我想要避免的。
    • 啊,对不起,我误会你了。
    • 因为我喜欢在自定义编组器实现中解包通道的纯粹想法,所以显示的代码不处理逗号。所以 Foo 和 Bar 的第一个值之间会有一个缺失。
    • 你是绝对正确的。解决这个问题的最好方法可能是生成一个结构并将其编组而不是附加字节。随意提出修改建议,我现在时间不多:(
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-01
    • 2016-06-18
    • 1970-01-01
    • 1970-01-01
    • 2011-09-15
    • 2013-08-01
    • 1970-01-01
    相关资源
    最近更新 更多