MarshalJSON没有同时在内存中的所有对象

时间:2013-08-08 18:58:34

标签: go marshalling

我想使用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
}

2 个答案:

答案 0 :(得分:2)

不幸的是,encoding/json包还没有办法做到这一点。你现在正在做什么(手动)是最好的方法,而无需修改内置包。

如果您要修补encoding/json,可以修改encoding/json/encode.go

中的reflectValueQuoted功能

您可能希望专注于数组案例(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中的频道做的不多,所以上面的内容可能还需要其他检查。

如果你最终走这条路线,你总是可以提交补丁。

答案 1 :(得分: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
}