附加到json文件而不写入整个文件

时间:2017-12-10 10:54:19

标签: json file go

我有一个json,其中包含一个属性值作为数组,我需要继续向数组附加值并写入文件。有没有办法可以避免重写现有数据,只附加新值?

-----在不同的主题上移动下一个问题--------------- 在结束过程中将大数据集写入文件增量文件写入或文件转储的推荐方法是什么?

2 个答案:

答案 0 :(得分:1)

如果您的JSON数组很简单,您可以使用类似下面的代码。在这段代码中,我手动创建了JSON数组。

type item struct {
    Name string
}

func main() {
    fd, err := os.Create("hello.json")
    if err != nil {
        log.Fatal(err)
    }

    fd.Write([]byte{'['})
    for i := 0; i < 10; i++ {
        b, err := json.Marshal(item{
            "parham",
        })
        if err != nil {
            log.Fatal(err)
        }

        if i != 0 {
            fd.Write([]byte{','})
        }
        fd.Write(b)
    }
    fd.Write([]byte{']'})
}

如果你想在每一步中都有一个有效的数组,你可以在每次迭代结束时写']',然后在下一次迭代的开始时寻找。

答案 1 :(得分:1)

如果现有JSON实际上是一个数组,或者它是一个将数组作为最后一个或唯一对的对象,那么一般解决方案最有意义,就像你的情况一样。否则,您插入而不是追加。您可能不希望读取整个文件。

一种方法与您的想法没什么不同,但处理了几个细节

  1. 阅读文件末尾以确认它“以数组结尾”
  2. 保留该部分
  3. 将文件放在该结束数组括号
  4. 从标准编码器输出新数据数组,删除其左括号,并在必要时插入逗号
  5. 新输出的结尾替换原始结束数组括号
  6. 将尾巴的其余部分重新打开
  7. $records = DB::table('assets')
                    ->join('client', 'client.Id', '=', 'assets.client_id')
                    ->join('property', 'property.Id', '=', 'assets.property_id')
                    ->select('assets.client_id','client.firstname','client.lastname','property.name','assets.investment_type')
                    ->orderBy('assets.client_id')
                    ->get();
    

    然后用法就像在这个测试片段中一样

    import (
            "bytes"
            "errors"
            "io"
            "io/ioutil"
            "os"
            "regexp"
            "unicode"
    )
    
    const (
            tailCheckLen = 16
    )
    
    var (
            arrayEndsObject = regexp.MustCompile("(\\[\\s*)?](\\s*}\\s*)$")
            justArray       = regexp.MustCompile("(\\[\\s*)?](\\s*)$")
    )
    
    type jsonAppender struct {
            f               *os.File
            strippedBracket bool
            needsComma      bool
            tail            []byte
    }
    
    func (a jsonAppender) Write(b []byte) (int, error) {
            trimmed := 0
            if !a.strippedBracket {
                    t := bytes.TrimLeftFunc(b, unicode.IsSpace)
                    if len(t) == 0 {
                            return len(b), nil
                    }
                    if t[0] != '[' {
                            return 0, errors.New("not appending array: " + string(t))
                    }
                    trimmed = len(b) - len(t) + 1
                    b = t[1:]
                    a.strippedBracket = true
            }
            if a.needsComma {
                    a.needsComma = false
                    n, err := a.f.Write([]byte(", "))
                    if err != nil {
                            return n, err
                    }
            }
            n, err := a.f.Write(b)
            return trimmed + n, err
    }
    
    func (a jsonAppender) Close() error {
            if _, err := a.f.Write(a.tail); err != nil {
                    defer a.f.Close()
                    return err
            }
            return a.f.Close()
    }
    
    func JSONArrayAppender(file string) (io.WriteCloser, error) {
            f, err := os.OpenFile(file, os.O_RDWR, 0664)
            if err != nil {
                    return nil, err
            }
    
            pos, err := f.Seek(0, io.SeekEnd)
            if err != nil {
                    return nil, err
            }
    
            if pos < tailCheckLen {
                    pos = 0
            } else {
                    pos -= tailCheckLen
            }
            _, err = f.Seek(pos, io.SeekStart)
            if err != nil {
                    return nil, err
            }
    
            tail, err := ioutil.ReadAll(f)
            if err != nil {
                    return nil, err
            }
    
            hasElements := false
    
            if len(tail) == 0 {
                    _, err = f.Write([]byte("["))
                    if err != nil {
                            return nil, err
                    }
            } else {
                    var g [][]byte
                    if g = arrayEndsObject.FindSubmatch(tail); g != nil {
                    } else if g = justArray.FindSubmatch(tail); g != nil {
                    } else {
                            return nil, errors.New("does not end with array")
                    }
    
                    hasElements = len(g[1]) == 0
                    _, err = f.Seek(-int64(len(g[2])+1), io.SeekEnd) // 1 for ]
                    if err != nil {
                            return nil, err
                    }
                    tail = g[2]
            }
    
            return jsonAppender{f: f, needsComma: hasElements, tail: tail}, nil
    }
    

    您可以使用所需编码器上的任何设置。唯一的硬编码部分是处理 a, err := JSONArrayAppender(f) if err != nil { t.Fatal(err) } added := []struct { Name string `json:"name"` }{ {"Wonder Woman"}, } if err = json.NewEncoder(a).Encode(added); err != nil { t.Fatal(err) } if err = a.Close(); err != nil { t.Fatal(err) } ,但您可以为此添加一个参数。