Golang中如何将JSON对象保存到平面文件

Golang中如何将JSON对象保存到平面文件 我有一个需求,需要将JSON保存下来,并在解组后修改结构体的字段,然后写回到同一个文件中。

假设我有以下结构体

type struct1 struct{
St1B	bool
St1I	int
}
type struct2 struct{
St2S	string
St2B	bool
}

我初始化这些值

pp := &struct1{
St1B : true,
St1I : 23,
}
dd := &struct2{
St2S : "Hello",
St2B : false,
}

将它们插入到如下所示的映射中后

Configs = make(map[string]interface{})
Configs["st1"] = pp
Configs["st2"] = dd

我应该如何将其写入JSON文件并读取它,以便它能正确解组到相应的结构体?

我尝试了以下方法,但它只会处理"pp"结构体,而不会处理其他结构体如"dd"或其他可能存在的结构体。

jdata1, err := json.MarshalIndent(pp, "", " ")
if err != nil {
fmt.Println("error:", err)
}
fmt.Println(string(jdata1))
jsonFile, err := os.Create("./data.json")
jsonFile.Write(jdata1)

基本上,我试图将这些结构体的值保存到一个平面文件中,并能在修改之前检索它。 任何建议都将不胜感激。


更多关于Golang中如何将JSON对象保存到平面文件的实战教程也可以访问 https://www.itying.com/category-94-b0.html

9 回复

为便于记录,我已将您的代码放入Go Playground以便重现问题:https://play.golang.com/p/fGDUckeIPvf

更多关于Golang中如何将JSON对象保存到平面文件的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


只有通过实际操作才能更好地理解这些内容。感谢您帮助我理解。

mholt 的这个工具可以让你查看 JSON 对象如何映射到 Go 结构体类型。https://mholt.github.io/json-to-go/

这很容易做到:https://play.golang.com/p/kZyiphK6ve6

请查看encoding/json包的文档,其中解释了所有这些内容。

感谢 Iutzhorn,这个实现起来相当简单。作为这方面的新手,我现在打算尝试从 JSON 文件中读取数据并将其反序列化到两个不同的结构体中。如果能就如何实现这一点提供建议,那将非常有帮助。

仅序列化 Configs,而不仅仅是 pp

jdata1, err := json.MarshalIndent(Configs, "", " ")

结果:

{
 "st1": {
  "St1B": true,
  "St1I": 23
 },
 "st2": {
  "St2S": "Hello",
  "St2B": false
 }
}

参见 https://play.golang.com/p/v_66tYcLAlI

好的,我可能理解有误,但我想实现的是:https://play.golang.com/p/g3clTg9pfiA

configs2 := struct2{}
err2 := json.Unmarshal([]byte(jsonBlob2), &configs2)

期望 configs2 能够被填充为 “struct2” 解组后的值。 目前实际结果是: %q { false} 这是 struct2 内部布尔类型的默认值。

为什么它不能直接解组到结构体中呢?

因为你尝试将这个JSON

{
  "st2": {
    "St2S": "Hello",
    "St2B": true
 }
}

反序列化到这个结构体:

type struct2 struct {
	St2S string
	St2B bool
}

你需要一个内部导出的struct2成员来接收st2

package main

import (
	"encoding/json"
	"fmt"
)

type struct2 struct {
	St2 struct2inner
}

type struct2inner struct {
	St2S string
	St2B bool
}

func main() {

	jsonBlob2 := `{
  "st2": {
    "St2S": "Hello",
    "St2B": true
 }
}`

	configs2 := struct2{}
	err2 := json.Unmarshal([]byte(jsonBlob2), &configs2)
	if err2 != nil {
		fmt.Println("error:", err2)
	}
	fmt.Println("%q", configs2)
}

运行结果:

%q {{Hello true}}

参考:https://play.golang.com/p/poNx2kFsg0p

要将包含不同类型结构体的映射保存为JSON文件并正确解组,可以使用以下方法:

package main

import (
	"encoding/json"
	"fmt"
	"os"
)

type struct1 struct {
	St1B bool
	St1I int
}

type struct2 struct {
	St2S string
	St2B bool
}

var Configs map[string]interface{}

func saveToFile(filename string) error {
	// 将整个Configs映射序列化为JSON
	data, err := json.MarshalIndent(Configs, "", "  ")
	if err != nil {
		return err
	}

	// 写入文件
	return os.WriteFile(filename, data, 0644)
}

func loadFromFile(filename string) error {
	// 读取文件内容
	data, err := os.ReadFile(filename)
	if err != nil {
		return err
	}

	// 临时存储解组后的数据
	tempConfigs := make(map[string]json.RawMessage)
	
	// 第一层解组到RawMessage
	if err := json.Unmarshal(data, &tempConfigs); err != nil {
		return err
	}

	// 重新初始化Configs
	Configs = make(map[string]interface{})

	// 根据键名分别解组到对应的结构体
	for key, raw := range tempConfigs {
		switch key {
		case "st1":
			var st1 struct1
			if err := json.Unmarshal(raw, &st1); err != nil {
				return err
			}
			Configs[key] = &st1
		case "st2":
			var st2 struct2
			if err := json.Unmarshal(raw, &st2); err != nil {
				return err
			}
			Configs[key] = &st2
		default:
			// 对于未知类型,保持原始JSON数据
			var unknown interface{}
			if err := json.Unmarshal(raw, &unknown); err != nil {
				return err
			}
			Configs[key] = unknown
		}
	}

	return nil
}

func main() {
	// 初始化数据
	pp := &struct1{
		St1B: true,
		St1I: 23,
	}

	dd := &struct2{
		St2S: "Hello",
		St2B: false,
	}

	Configs = make(map[string]interface{})
	Configs["st1"] = pp
	Configs["st2"] = dd

	// 保存到文件
	if err := saveToFile("config.json"); err != nil {
		fmt.Println("保存错误:", err)
		return
	}

	// 修改前读取文件
	if err := loadFromFile("config.json"); err != nil {
		fmt.Println("读取错误:", err)
		return
	}

	// 验证读取的数据
	if st1, ok := Configs["st1"].(*struct1); ok {
		fmt.Printf("读取的st1: St1B=%v, St1I=%d\n", st1.St1B, st1.St1I)
	}

	if st2, ok := Configs["st2"].(*struct2); ok {
		fmt.Printf("读取的st2: St2S=%s, St2B=%v\n", st2.St2S, st2.St2B)
	}

	// 修改数据示例
	if st1, ok := Configs["st1"].(*struct1); ok {
		st1.St1I = 100
		st1.St1B = false
	}

	if st2, ok := Configs["st2"].(*struct2); ok {
		st2.St2S = "Modified"
		st2.St2B = true
	}

	// 保存修改后的数据
	if err := saveToFile("config.json"); err != nil {
		fmt.Println("保存修改错误:", err)
		return
	}
}

生成的JSON文件内容:

{
  "st1": {
    "St1B": true,
    "St1I": 23
  },
  "st2": {
    "St2S": "Hello",
    "St2B": false
  }
}

这种方法的关键点:

  1. 使用json.RawMessage进行两阶段解组,先获取原始JSON数据,再根据键名分配到具体结构体
  2. loadFromFile函数中通过switch语句根据键名选择对应的结构体类型
  3. 保存时直接序列化整个Configs映射
  4. 读取时能正确恢复原始结构体类型,支持后续修改操作

这样就能实现将包含不同类型结构体的映射保存到文件,并在需要时正确读取和修改。

回到顶部