不要将不需要的JSON键值读入内存

时间:2014-07-28 19:13:10

标签: json go

我有一个带有单个字段的JSON文件,当加载到内存中时会占用巨大的空间。其他领域是合理的,但除非我绝对必须,否则我会注意不要加载该特定字段。

{
    "Field1": "value1",
    "Field2": "value2",
    "Field3": "a very very long string that potentially takes a few GB of memory"
}

将该文件读入内存时,我想忽略Field3(因为加载它可能会导致我的应用崩溃)。这里有一些我会假设的代码,因为它使用io流而不是将[]byte类型传递给Unmarshal命令。

package main

import (
    "encoding/json"
    "os"
)

func main() {
    type MyStruct struct {
        Field1 string
        Field2 string
    }
    fi, err := os.Open("myJSONFile.json")
    if err != nil {
        os.Exit(2)
    }
    // create an instance and populate
    var mystruct MyStruct
    err = json.NewDecoder(fi).Decode(&mystruct)
    if err != nil {
        os.Exit(2)
    }
    // do some other stuff
}

问题是内置json.Decoder类型会将整个文件读入Decode内存,然后丢弃与struct不匹配的键值&# 39; s字段(正如之前在StackOverflow上指出的那样:link)。

有没有办法在Go中解码JSON而不将整个JSON对象保留在内存中?

1 个答案:

答案 0 :(得分:2)

您可以编写一个自定义io.Reader,并将其提供给json.Decoder,并预先读取您的json文件并跳过该特定字段。

另一种选择是编写自己的解码器,更复杂和杂乱。

//编辑它似乎是一个有趣的练习,所以这里是:

type IgnoreField struct {
    io.Reader
    Field string
    buf   bytes.Buffer
}

func NewIgnoreField(r io.Reader, field string) *IgnoreField {
    return &IgnoreField{
        Reader: r,
        Field:  field,
    }
}
func (iF *IgnoreField) Read(p []byte) (n int, err error) {
    if n, err = iF.Reader.Read(p); err != nil {
        return
    }
    s := string(p)
    fl := `"` + iF.Field + `"`
    if i := strings.Index(s, fl); i != -1 {
        l := strings.LastIndex(s[0:i], ",")
        if l == -1 {
            l = i
        }
        iF.buf.WriteString(s[0:l])

        s = s[i+1+len(fl):]
        i = strings.Index(s, `"`)
        if i != -1 {
            s = s[i+1:]
        }
        for {
            i = strings.Index(s, `"`) //end quote
            if i != -1 {
                s = s[i+1:]
                fmt.Println("Skipped")
                break
            } else {
                if n, err = iF.Reader.Read(p); err != nil {
                    return
                }
                s = string(p)
            }
        }
        iF.buf.WriteString(s)
    }
    ln := iF.buf.Len()
    if ln >= len(p) {
        tmp := iF.buf.Bytes()
        iF.buf.Reset()
        copy(p, tmp[0:len(p)])
        iF.buf.Write(p[len(p):])
        ln = len(p)
    } else {
        copy(p, iF.buf.Bytes())
        iF.buf.Reset()
    }
    return ln, nil
}

func main() {
    type MyStruct struct {
        Field1 string
        Field2 string
    }
    fi, err := os.Open("myJSONFile.json")
    if err != nil {
         os.Exit(2)
    }
    // create an instance and populate
    var mystruct MyStruct
    err := json.NewDecoder(NewIgnoreField(fi, "Field3")).Decode(&mystruct)
    if err != nil {
        fmt.Println(err)
    }
    fmt.Println(mystruct)
}

playground