go语言使用json的方法实现-kb88凯时官网登录

来自:网络
时间:2024-08-28
阅读:
免费资源网,https://freexyz.cn/

在go语言中,处理json数据通常涉及编码(将go结构体转换为json字符串)和解码(将json字符串转换为go结构体)。go标准库中的encoding/json包提供了这些功能。第三方插件可以使用"github.com/goccy/go-json"也有同样的功能

 marshal函数将会递归遍历整个对象,依次按成员类型对这个对象进行编码,类型转换规则如下:

  • bool类型 转换为jsonboolean

  • 整数,浮点数等数值类型 转换为jsonnumber

  • string 转换为json的字符串(带""引号)

  • struct 转换为jsonobject,再根据各个成员的类型递归打包

  • 数组或切片 转换为jsonarray

  • []byte 会先进行base64编码然后转换为json字符串

  • map转换为jsonobjectkey必须是string

  • interface{} 按照内部的实际类型进行转换

  • nil 转为jsonnull

  • channel,func等类型 会返回unsupportedtypeerror

 1、使用标准库中的encoding/json包

字符串输出&格式化输出&解码

package main
import (
	"encoding/json"
	"fmt"
)
type colorgroup struct {
	id     int
	name   string
	colors []string
}
// 创建一个colorgroup类型的变量来保存解码后的数据
var decodedgroup colorgroup
func main() {
	group := colorgroup{
		id:     1,
		name:   "reds",
		colors: []string{"crimson", "red", "ruby", "maroon"},
	}
	// 将结构体编码为json字符串
	jsondata1, err := json.marshal(group)
	jsondata2, err := json.marshalindent(group, "", "	")
	if err != nil {
		fmt.println("error:", err)
		return
	}
	// 打印json字符串
	fmt.println(string(jsondata1))
	fmt.println(string(jsondata2))
	// output:
	//{"id":1,"name":"reds","colors":["crimson","red","ruby","maroon"]}
	//	{
	//		"id": 1,
	//		"name": "reds",
	//		"colors": [
	//			"crimson",
	//			"red",
	//			"ruby",
	//			"maroon"
	//		]
	//	}
	// 将json字符串解码到colorgroup结构体中
	err = json.unmarshal([]byte(jsondata1), &decodedgroup)
	if err != nil {
		fmt.println("error:", err)
		return
	}
	// 打印解码后的数据
	fmt.printf("id: %d, name: %s, colors: %v\n", decodedgroup.id, decodedgroup.name, decodedgroup.colors)
	// output: id: 1, name: reds, colors: [crimson red ruby maroon]
	fmt.println(decodedgroup.colors[0])
	fmt.println(decodedgroup.colors[1])
}

 2、使用第三方包

标准输出&格式化输出&解码

package main
import (
	"fmt"
	"github.com/goccy/go-json"
	"os"
)
type colorgroup struct {
	id     int
	name   string
	colors []string
}
func main() {
	group := colorgroup{
		id:     1,
		name:   "reds",
		colors: []string{"crimson", "red", "ruby", "maroon"},
	}
	b1, err := json.marshal(group)
	if err != nil {
		fmt.println("error:", err)
	}
	println(os.stdout.write(b1)) //os.stdout.write(b1)将字节切片b(即json字符串的字节表示)写入到标准输出
	fmt.println("---------------格式化输出----------------")
	// 使用 marshalindent 来格式化输出
	b2, err := json.marshalindent(group, "", "  ") // 第二个参数是空字符串,表示不添加前缀;第三个参数是缩进字符串
	if err != nil {
		fmt.println("error:", err)
		return
	}
	// 使用 fmt.println 来打印字符串
	// marshalindent返回的是字节切片,我们需要使用string(b2)来将其转换为字符串
	fmt.println(string(b2)) // 将字节切片转换为字符串并打印
	// 输出将会是格式化后的 json 字符串
	// 创建一个colorgroup类型的变量来保存解码后的数据
	var decodedgroup colorgroup
	// 将json字符串解码到colorgroup结构体中
	err = json.unmarshal([]byte(b1), &decodedgroup)
	if err != nil {
		fmt.println("error:", err)
		return
	}
	// 打印解码后的数据
	fmt.printf("id: %d, name: %s, colors: %v\n", decodedgroup.id, decodedgroup.name, decodedgroup.colors)
	// output: id: 1, name: reds, colors: [crimson red ruby maroon]
	fmt.println(decodedgroup.colors[0])
	fmt.println(decodedgroup.colors[1])
}

请注意,在解码时,你需要将json字符串转换为[]byte,并且传入结构体的指针(使用&)。这样,解码后的数据才会被写入到结构体中 

3、decode  

package main
import (
	"fmt"
	"github.com/goccy/go-json"
)
// animal 定义结构体来表示单个json对象
type animal struct {
	name  string
	order string
}
func main() {
	//创建一个json字节切片
	var jsonblob = []byte(`[   
		{"name": "platypus", "order": "monotremata"},   
		{"name": "quoll", "order": "dasyuromorphia"}   
	]`)
	var animals []animal
	err := json.unmarshal(jsonblob, &animals)
	if err != nil {
		fmt.println("error:", err)
	}
	fmt.printf("% v", animals)
	fmt.println()
	// 打印解码后的数据
	for _, animal := range animals {
		fmt.printf("name: %s, order: %s\n", animal.name, animal.order)
	}
}

4、注意

结构体

结构体必须是大写字母开头的成员才会被json处理到,小写字母开头的成员不会有影响。

mashal时,结构体的成员变量名将会直接作为json objectkey打包成jsonunmashal时,会自动匹配对应的变量名进行赋值,大小写不敏感。

unmarshal时,如果json中有多余的字段,会被直接抛弃掉;如果json缺少某个字段,则直接忽略不对结构体中变量赋值,不会报错。

package main
import (
	"encoding/json"
	"fmt"
)
type message struct {
	name  string
	body  string
	time  int64
	inner string
}
func main() {
	var m = message{
		name:  "alice",
		body:  "hello",
		time:  1294706395881547000,
		inner: "ok",
	}
	b := []byte(`{"name":"bob","food":"pickle", "inner":"changed"}`)
	err := json.unmarshal(b, &m)
	if err != nil {
		fmt.printf(err.error())
		return
	}
	fmt.printf("%v", m)
    //output: {bob hello 1294706395881547000 ok}
}

structtag/结构体标签

如果希望手动配置结构体的成员和json字段的对应关系,可以在定义结构体的时候给成员打标签:

使用omitempty熟悉,如果该字段为nil或0值(数字0,字符串"",空数组[]等),则打包的json结果不会有这个字段。

案例一

package main
import (
	"encoding/json"
	"fmt"
)
type message struct {
	name string `json:"msg_name"`       // 对应json的msg_name
	body string `json:"body,omitempty"` // 如果为空置则忽略字段
	time int64  `json:"-"`              // 直接忽略字段
}
func main() {
	var m = message{
		name: "alice",
		body: "",
		time: 1294706395881547000,
	}
	data, err := json.marshal(m)
	if err != nil {
		fmt.printf(err.error())
		return
	}
	fmt.println(string(data))
	//output:{"msg_name":"alice"}
}

案例二 

package main
import (
	"encoding/json"
	"fmt"
	"log"
	"time"
)
// 定义一个用于json映射的结构体
type user struct {
	name     string     `json:"username"` // 自定义字段名称映射
	email    string     `json:"email"`
	lastseen customtime `json:"last_seen"` // 嵌套对象
	active   bool       `json:"-"`         // 忽略此字段,即使json中存在也不解码
}
// customtime 是一个用于表示时间的结构体
type customtime struct {
	time.time
}
// 实现 json.unmarshaler 接口的 unmarshaljson 方法
func (ct *customtime) unmarshaljson(data []byte) error {
	var s string
	if err := json.unmarshal(data, &s); err != nil {
		return err
	}
	// 解析自定义时间格式
	parsedtime, err := time.parse(time.rfc3339, s)
	if err != nil {
		return err
	}
	ct.time = parsedtime
	return nil
}
func main() {
	// 模拟从http请求中获取的json数据
	jsondata := []byte(`{
		"username": "johndoe",
		"email": "john.doe@example.com",
		"last_seen": "2023-04-01t12:34:56z",
		"active": true
	}`)
	// 创建一个 user 实例
	var user user
	// 使用 json.unmarshal 解码 json 数据
	if err := json.unmarshal(jsondata, &user); err != nil {
		log.fatal("error unmarshaling json:", err)
	}
	// 打印解码后的信息
	fmt.printf("name: %s\n", user.name)
	fmt.printf("email: %s\n", user.email)
	fmt.printf("last seen: %v\n", user.lastseen)
	// active 字段将不会被解码,即使json中存在
	fmt.printf("active: %v\n", user.active)
	//输出:
	//name: johndoe
	//email: john.doe@example.com
	//	last seen: 2023-04-01 12:34:56  0000 utc
	//active: false
}

5、更灵活地使用json

使用json.rawmessage

json.rawmessage其实就是[]byte类型的重定义。可以进行强制类型转换。

现在有这么一种场景,结构体中的其中一个字段的格式是未知的:

type command struct {
	id   int
	cmd  string
	args *json.rawmessage
}

使用json.rawmessage的话,args字段在unmarshal时不会被解析,直接将字节数据赋值给args。我们可以能先解包第一层的json数据,然后根据cmd的值,再确定args的具体类型进行第二次unmarshal

这里要注意的是,一定要使用指针类型*json.rawmessage,否则在args会被认为是[]byte类型,在打包时会被打包成base64编码的字符串。

案例一

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type command struct {
	id   int
	cmd  string
	args *json.rawmessage // 未解析的json片段
}
func main() {
	//json字节切片
	jsondata := []byte(`{  
		"id": 1,  
		"cmd": "example",  
		"args": ["arg1", "arg2"]  
	}`)
	var cmd command
	//解码/反序列化
	if err := json.unmarshal(jsondata, &cmd); err != nil {
		log.fatalf("error unmarshaling json: %v", err)
	}
	fmt.printf("command: % v\n", cmd)
	// 如果需要,可以进一步处理cmd.args字段
	// 例如,将其解析为特定的go类型
	var args []string
	if err := json.unmarshal(*cmd.args, &args); err != nil {
		log.printf("解析错误: %v", err)
	} else {
		fmt.printf("args: %v\n", args)
	}
    //输出
    //command: {id:1 cmd:example args:0xc0000080f0}
    //args: [arg1 arg2]
}

案例二

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type command struct {
	id   int
	cmd  string
	args *json.rawmessage // 未解析的json片段
}
// unmarshaljson 自定义json解码方法,command实现了unmarshaler接口
func (c *command) unmarshaljson(data []byte) error {
	fmt.println("--------------使用自定义解码--------------")
	// 定义一个辅助结构体,用于解码除args外的其他字段
	type alias command
	var aux struct {
		alias // 嵌入别名类型以获取其他字段
	}
	// 先解码除args外的所有字段
	if err := json.unmarshal(data, &aux); err != nil {
		return err
	}
	fmt.printf("command id: % v, cmd: % v\n", aux.alias.id, aux.alias.cmd)
	// 将别名结构体中的字段复制到c中
	*c = command(aux.alias)
	// 检查json中是否有args字段,并处理它
	var m map[string]json.rawmessage
	if err := json.unmarshal(data, &m); err != nil {
		// 如果这里出错,可能是因为json格式不正确,但我们可能仍然想要保留已经解析的字段
		// 因此,我们可以只记录一个错误,但不返回它
		log.printf("error parsing args field: %v", err)
	} else {
		// 如果args字段存在,将其赋值给c.args
		if rawargs, ok := m["args"]; ok {
			c.args = &rawargs // 注意这里我们取了rawargs的地址
			var args []string
			if err := json.unmarshal(*c.args, &args); err != nil {
				log.printf("error parsing args contents: %v", err)
			} else {
				fmt.printf("args: %v\n", args)
			}
		}
	}
	// 如果没有错误,返回nil
	return nil
}
func main() {
	//json字节切片
	jsondata := []byte(`{  
		"id": 1,  
		"cmd": "example",  
		"args": ["arg1", "arg2"]  
	}`)
	var cmd command
	//解码/反序列化
	if err := json.unmarshal(jsondata, &cmd); err != nil {
		log.fatalf("error unmarshaling json: %v", err)
	}
}

案例三 

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type command struct {
	id   int
	cmd  string
	args *json.rawmessage // 未解析的json片段
}
// unmarshaljson 自定义json解码方法,command实现了unmarshaler接口
func (c *command) unmarshaljson(data []byte) error {
	fmt.println("--------------使用自定义解码--------------")
	// 检查json中是否有args字段,并处理它
	var m map[string]json.rawmessage
	if err := json.unmarshal(data, &m); err != nil {
		// 如果这里出错,可能是因为json格式不正确,但我们可能仍然想要保留已经解析的字段
		// 因此,我们可以只记录一个错误,但不返回它
		log.printf("error parsing args field: %v", err)
	} else {
		// 如果args字段存在,将其赋值给c.args
		if rawargs, ok := m["args"]; ok {
			c.args = &rawargs // 注意这里我们取了rawargs的地址
			var args []string
			if err := json.unmarshal(*c.args, &args); err != nil {
				log.printf("error parsing args contents: %v", err)
			} else {
				fmt.printf("args: %v\n", args)
			}
		}
	}
	// 如果没有错误,返回nil
	return nil
}
func main() {
	//json字节切片
	jsondata := []byte(`{  
		"id": 1,  
		"cmd": "example",  
		"args": ["arg1", "arg2"]  
	}`)
	var cmd command
	//解码/反序列化
	if err := json.unmarshal(jsondata, &cmd); err != nil {
		log.fatalf("error unmarshaling json: %v", err)
	}
}

 调用的json.unmarshal,并不是调用json.unmarshaler,为什么会调用unmarshaljson

调用 json.unmarshal 函数时,您并没有直接调用 json.unmarshaler 接口的方法。但是,json.unmarshal 函数在内部会检查目标类型是否实现了 json.unmarshaler 接口。如果实现了该接口,json.unmarshal 就会使用您为该类型定义的 unmarshaljson 方法来解码 json 数据。

这是 json.unmarshal 函数内部逻辑的一部分,用于确定如何解码 json 数据。具体步骤如下:

  • json.unmarshal 接收一个字节切片(包含 json 数据)和一个目标值的指针。
  • 它首先会检查目标值的类型是否实现了 json.unmarshaler 接口。
  • 如果实现了 json.unmarshaler 接口,json.unmarshal 就会调用该类型的 unmarshaljson 方法,并将 json 数据的字节切片作为参数传递给它。
  • 如果目标值没有实现 json.unmarshaler 接口,json.unmarshal 就会使用默认的解码逻辑来填充目标值的字段。

这种机制使得开发者能够灵活地控制 json 数据到 go 结构体之间的转换过程。通过实现 json.unmarshaler 接口,您可以:

  • 处理 json 数据中不存在的字段。
  • 自定义字段名称的映射规则。
  • 处理 json 数据中的嵌套对象或数组。
  • 执行额外的验证或数据处理逻辑。

以下是简单的示例,展示了如何为一个类型实现 json.unmarshaler 接口

处理 json 数据中不存在的字段

假设我们有一个结构体,它能够处理json中可能缺失的字段,并且为这些字段提供默认值。 

在这个例子中,age 字段在json中不存在,因此它将被赋予其类型的零值(对于int类型是0)。

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type user struct {
	name  string `json:"name"`
	age   int    `json:"age"`
	email string `json:"email,omitempty"`
}
func main() {
	var user user
	// json 中没有 "age" 字段,将使用 age 的零值 0
	jsondata := []byte(`{"name": "john", "email": "john@example.com"}`)
	if err := json.unmarshal(jsondata, &user); err != nil {
		log.fatal(err)
	}
	fmt.printf("name: %s, age: %d, email: %s\n", user.name, user.age, user.email)
	//name: john, age: 0, email: john@example.com
}

 自定义字段名称的映射规则

使用结构体标签中的json键来指定json字段名。

在这个例子中,结构体的字段名和json字段名不匹配,我们通过在结构体标签中指定json来实现映射。

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type user struct {
	username string `json:"user_name"`
	password string `json:"pass"`
}
func main() {
	var user user
	jsondata := []byte(`{"user_name": "johndoe", "pass": "secret"}`)
	if err := json.unmarshal(jsondata, &user); err != nil {
		log.fatal(err)
	}
	fmt.printf("username: %s, password: %s\n", user.username, user.password)
	//username: johndoe, password: secret
}

 处理 json 数据中的嵌套对象或数组

解码一个包含嵌套结构体的json数据。

在这个例子中,address 是一个嵌套在 user 结构体中的对象。

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type address struct {
	city    string `json:"city"`
	country string `json:"country"`
}
type user struct {
	name    string  `json:"name"`
	address address `json:"address"` // 嵌套对象
}
func main() {
	var user user
	jsondata := []byte(`{"name": "jane", "address": {"city": "new york", "country": "usa"}}`)
	if err := json.unmarshal(jsondata, &user); err != nil {
		log.fatal(err)
	}
	fmt.printf("name: %s, lives in %s, %s\n", user.name, user.address.city, user.address.country)
	//name: jane, lives in new york, usa
}

 执行额外的验证或数据处理逻辑

unmarshaljson方法中添加额外的验证逻辑。

在这个例子中,我们为user类型实现了自定义的unmarshaljson方法。在解码过程中,如果age字段的值是负数,将返回一个错误,这是一个额外的验证逻辑。

package main
import (
	"encoding/json"
	"fmt"
	"log"
)
type user struct {
	name string `json:"name"`
	age  int    `json:"age"`
}
func (u *user) unmarshaljson(data []byte) error {
	type alias user                         // 影子类型,避免递归调用 unmarshaljson
	aux := &alias{name: u.name, age: u.age} // 使用辅助结构体来解耦
	if err := json.unmarshal(data, aux); err != nil {
		return err
	}
	*u = user(*aux) // 将解耦的结构体赋值给当前结构体
	if u.age < 0 {  //年龄不能为负数
		return fmt.errorf("age cannot be negative")
	}
	return nil
}
func main() {
	var user user
	jsondata := []byte(`{"name": "alice", "age": -5}`)
	if err := json.unmarshal(jsondata, &user); err != nil {
		log.fatal(err)
	}
	fmt.printf("name: %s, age: %d\n", user.name, user.age)
}

 在上面的示例中,user类型实现了 json.unmarshaler 接口的 unmarshaljson 方法,使得 json.unmarshal 函数在解码 json 数据时会调用这个方法,而不是使用默认的解码逻辑。这允许我们自定义解码逻辑,例如只接受特定格式的 json 数据。 

使用interface{}

interface{}类型在unmarshal时,会自动将json转换为对应的数据类型:

json的boolean 转换为bool
json的数值 转换为float64
json的字符串 转换为string
json的array 转换为[]interface{}
json的object 转换为map[string]interface{}
json的null 转换为nil

需要注意的有两个。一个是所有的json数值自动转换为float64类型,使用时需要再手动转换为需要的intint64等类型。第二个是jsonobject自动转换为map[string]interface{}类型,访问时直接用json ``object的字段名作为key进行访问。再不知道json数据的格式时,可以使用interface{}

免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图