英文:
How to append consecutively to a JSON file in Go?
问题
我想知道如何在Go中连续写入同一个文件。我需要使用 os.WriteAt()
吗?
JSON基本上只是一个填充有结构体的数组:
[
{
"Id": "2817293",
"Data": "XXXXXXX"
},
{
"Id": "2817438",
"Data": "XXXXXXX"
}
...
]
我想要连续地向其中写入数据,也就是在关闭文件之前多次向该JSON数组追加数据。
我想要写入文件的数据是一个包含上述结构体的切片:
dataToWrite := []struct{
Id string
Data string
}{}
在Go中,如何正确地连续写入JSON数组呢?
我的当前方法会在JSON文件中创建多个切片,这不是我想要的结果。写入过程(位于一个循环中)如下所示:
...
// 读取文件的当前状态
data := []byte{}
f.Read(data)
// 将当前状态写入切片
curr := []Result{}
json.Unmarshal(data, &curr)
// 将数据追加到已创建的切片中
curr = append(curr, *initArr...)
JSON, _ := JSONMarshal(curr)
// 清空数据容器
initArr = &[]Result{}
// 写入
_, err := f.Write(JSON)
if err != nil {
log.Fatal(err)
}
...
英文:
I wonder how can I write consecutively to the same file in Go? Do I have to use os.WriteAt()
?
The JSON is basically just an array filled with structs:
[
{
"Id": "2817293",
"Data": "XXXXXXX"
},
{
"Id": "2817438",
"Data": "XXXXXXX"
}
...
]
I want right data to it consecutively i.e. append to that JSON array more than once before closing the file.
The data I want to write to the file is a slice of said structs:
dataToWrite := []struct{
Id string
Data string
}{}
What is the proper way to write consecutively to a JSON array in Go?
My current approach creates multiple slices in the JSON file and thus is not what I want. The write process (lying in a for loop) looks like this:
...
// Read current state of file
data := []byte{}
f.Read(data)
// Write current state to slice
curr := []Result{}
json.Unmarshal(data, &curr)
// Append data to the created slice
curr = append(curr, *initArr...)
JSON, _ := JSONMarshal(curr)
// Empty data container
initArr = &[]Result{}
// Write
_, err := f.Write(JSON)
if err != nil {
log.Fatal(err)
}
...
答案1
得分: 2
写入文件时,先写入开头的[
符号。在文件上创建一个编码器。循环遍历切片和每个切片的元素。如果不是第一个切片元素,则写入逗号。使用编码器对每个切片元素进行编码。最后写入结尾的]
符号。
_, err := f.WriteString("[")
if err != nil {
log.Fatal(err)
}
e := json.NewEncoder(f)
first := true
for i := 0; i < 10; i++ {
// 为此迭代创建虚拟切片数据。
dataToWrite := []struct {
Id string
Data string
}{
{fmt.Sprintf("id%d.1", i), fmt.Sprintf("data%d.1", i)},
{fmt.Sprintf("id%d.2", i), fmt.Sprintf("data%d.2", i)},
}
// 将每个切片元素编码到文件中
for _, v := range dataToWrite {
// 如果不是第一个元素,则写入逗号分隔符。
if !first {
_, err := f.WriteString(",\n")
if err != nil {
log.Fatal(err)
}
}
first = false
err := e.Encode(v)
if err != nil {
log.Fatal(err)
}
}
}
_, err = f.WriteString("]")
if err != nil {
log.Fatal(err)
}
如果合理的话,可以将所有切片元素都保存在内存中,然后通过一次性编码所有数据来简化代码:
type Item struct {
Id string
Data string
}
// 收集要写入的所有项。
var result []Item
for i := 0; i < 10; i++ {
// 为此迭代生成切片。
dataToWrite := []Item{
{fmt.Sprintf("id%d.1", i), fmt.Sprintf("data%d.1", i)},
{fmt.Sprintf("id%d.2", i), fmt.Sprintf("data%d.2", i)},
}
// 将此迭代生成的切片附加到结果中。
result = append(result, dataToWrite...)
}
// 将结果写入文件。
err := json.NewEncoder(f).Encode(result)
if err != nil {
log.Fatal(err)
}
请注意,以上代码示例中的f
和log
变量需要根据实际情况进行定义和处理。
英文:
Write the opening [
to the file. Create an encoder on the file. Loop over slices and the elements of each slice. Write a comma if it's not the first slice element. Encode each slice element with the encoder. Write the closing ]
.
_, err := f.WriteString("[")
if err != nil {
log.Fatal(err)
}
e := json.NewEncoder(f)
first := true
for i := 0; i < 10; i++ {
// Create dummy slice data for this iteration.
dataToWrite := []struct {
Id string
Data string
}{
{fmt.Sprintf("id%d.1", i), fmt.Sprintf("data%d.1", i)},
{fmt.Sprintf("id%d.2", i), fmt.Sprintf("data%d.2", i)},
}
// Encode each slice element to the file
for _, v := range dataToWrite {
// Write comma separator if not the first.
if !first {
_, err := f.WriteString(",\n")
if err != nil {
log.Fatal(err)
}
}
first = false
err := e.Encode(v)
if err != nil {
log.Fatal(err)
}
}
}
_, err = f.WriteString("]")
if err != nil {
log.Fatal(err)
}
https://go.dev/play/p/Z-T1nxRIaqL
If it's reasonable to hold all of the slice elements in memory, then simplify the code by encoding all of the data in a single batch:
type Item struct {
Id string
Data string
}
// Collect all items to write in this slice.
var result []Item
for i := 0; i < 10; i++ {
// Generate slice for this iteration.
dataToWrite := []Item{
{fmt.Sprintf("id%d.1", i), fmt.Sprintf("data%d.1", i)},
{fmt.Sprintf("id%d.2", i), fmt.Sprintf("data%d.2", i)},
}
// Append slice generated in this iteration to the result.
result = append(result, dataToWrite...)
}
// Write the result to the file.
err := json.NewEncoder(f).Encode(result)
if err != nil {
log.Fatal(err)
}
答案2
得分: 1
如果您不关心现有文件,可以像@redblue提到的那样,对整个切片使用Encoder.Encode
。
如果您有一个现有的文件要追加,最简单的方法是按照您在编辑中展示的方式:将整个文件Unmarshal
或Decoder.Decoder
为一个结构体切片,将新的结构体追加到切片中,然后使用Marshal
或Encoder.Encode
重新编码整个切片。
如果您有大量的数据,您可能希望考虑使用JSON Lines来避免尾随的,
和]
问题,并且每行写入一个JSON对象。或者您可以使用常规的JSON,从文件末尾回溯,这样您就可以覆盖最后的]
,然后写入一个,
,新的JSON编码结构体,最后再写入一个]
,使文件再次成为有效的JSON数组。
因此,取决于您的用例和数据大小,您可以选择哪种方法。
英文:
If you don't care about the existing file you can just use Encoder.Encode
on the whole slice as @redblue mentioned.
If you have an existing file you want to append to, the simplest way is to do what you've shown in your edit: Unmarshal
or Decoder.Decoder
the whole file into a slice of structs, append the new struct to the slice, and re-decode the whole lot using Marshal
or Encoder.Encode
.
If you have a large amount of data, you may want to consider using JSON Lines to avoid the trailing ,
and ]
issue, and write one JSON object per line. Or you could use regular JSON, seek back from the end of the file so you're writing over the final ]
, then write a ,
, the new JSON-encoded struct, and finally a ]
to make the file a valid JSON array again.
So it depends on a bit on your use case and the data size which approach you take.
答案3
得分: 0
注意
这个答案是一个关于如何处理现有文件内容的“解决方案”或“解决方法”!
这意味着它允许你向由你的API创建的“json”文件中追加内容。
显然,这仅适用于具有相同结构的数组。
实际工作的json格式:
[
object,
...
object,
]
在写入文件时,不要写入[
和]
。
只需将序列化的json
对象追加到文件中,并添加一个逗号。
实际文件内容:
object,
...
object,
最后,在读取文件时,在前面添加[
,在后面添加]
。
这样,你可以从多个来源向文件中写入内容,并仍然得到有效的JSON。
同时,加载文件时,你可以为你的JSON处理器提供有效的输入。
我们就是这样编写我们的日志文件,并通过REST调用提供一个有效的JSON,然后通过JavaScript Grid进行处理。
英文:
NOTICE
This Answer is a solution
or workaround
if you care about the content of an existing file!
This means it allows you to append to an existing
json
file, created by your API
.
Obviously this only works for arrays of same structs
actual working json format:
[
object,
...
object,
]
When writing to a file, do NOT write [
and ]
.
Just append to the file writing your serialized json
object and append a ,
actual file content:
object,
...
object,
Finally when reading the file prepend [
and append ]
This way you can write
to the file from multiple sources and still have valid JSON
Also load the file and have a valid input for your json
-processor.
We write our logfiles like this and provide a vaild json
via REST
calls, which is then processed (for example by a JavaScript
Grid
)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论