这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战
看 JSON 处理 代码
import (
"encoding/json"
"errors"
"fmt"
"reflect"
"strings"
"testing"
jsoniter "github.com/json-iterator/go"
)
// json化
func JsonMarshal(v interface{}) string {
var fasterJson = jsoniter.ConfigCompatibleWithStandardLibrary
b, err := fasterJson.Marshal(v)
if err != nil {
logs.Error("JsonMarshal failed for v: %+v", v)
return `{"err_msg": "not json format"}`
}
return string(b)
}
// 反json化, jsonStr是json化的字符串,v传空的结构体
func JsonUnmarshal(jsonStr string, v interface{}) error {
var fasterJson = jsoniter.ConfigCompatibleWithStandardLibrary
byteValue := []byte(jsonStr)
err := fasterJson.Unmarshal(byteValue, v)
if err != nil {
logs.Error("JsonUnmarshal failed for v: %+v, err = %s", v, err.Error())
return errors.New("JsonUnmarshal failed for v: %+v")
}
return nil
}
字符串为空的处理
json 包在对空字符串的处理,非常容易出错
"" 与”{}"
type User struct {
Name string
FansCount int64
}
func TestJsonMashal3(t *testing.T) {
str := "{}"
var user User
err := JsonUnmarshal(str, &user)
fmt.Println(err, user)
str1 := ""
var user1 User
err1 := JsonUnmarshal(str1, &user)
fmt.Println(err1, user1)
}
执行结果:
=== RUN TestJsonMashal3
<nil> { 0}
JsonUnmarshal failed for v: %+v { 0}
--- PASS: TestJsonMashal3 (0.00s)
PASS
可以看到 在处理 空字符串“” 时 程序抛出异常 JsonUnmarshal failed for v: %+v { 0}
但是如果是 空 json "{}" 发现是可以处理的, 返回的结果是 { 0}
字段为空的处理
type Person struct {
Name string
Age int64
Birth time.Time
Children []Person
}
func TestJsonMashalEmpty(t *testing.T) {
person := Person{}
jsonBytes:= JsonMarshal(person)
fmt.Println(string(jsonBytes)) //{"Name":"","Age":0,"Birth":"0001-01-01T00:00:00Z","Children":null}
}
执行结果
=== RUN TestJsonMashal4
{"Name":"","Age":0,"Birth":"0001-01-01T00:00:00Z","Children":null}
--- PASS: TestJsonMashal4 (0.00s)
PASS
发现没,当是 struct 中 的字段没有值时, 使用json.Marshal 方法并不会忽略这些字段,而是输出了他们的默认空值, 这个往往和我们的预期不一致。int 和 float 类型零值是 0,string 类型零值是 "",对象类型零值是 nil。 有没有办法忽略这些空字段。有, 加一个 omitempty tag 就可以了。
type PersonAllowEmpty struct {
Name string `json:",omitempty"`
Age int64 `json:",omitempty"`
Birth time.Time `json:",omitempty"`
Children []PersonAllowEmpty `json:",omitempty"`
}
func TestMarshalEmpty2(t *testing.T) {
person := PersonAllowEmpty{}
jsonBytes := JsonMarshal(person)
fmt.Println(string(jsonBytes)) // {"Birth":"0001-01-01T00:00:00Z"}
}
运行结果
=== RUN TestMarshalEmpty2
{"Birth":"0001-01-01T00:00:00Z"}
--- PASS: TestMarshalEmpty2 (0.00s)
PASS
字段在任何情况下都被 json 忽略要怎么做?
type Person struct {
Name string `json:"-"`
Age int64 `json:"-"`
Birth time.Time `json:"-"`
Children []string `json:"-"`
}
func main() {
birth, _ := time.Parse(time.RFC3339, "1988-12-02T15:04:27+08:00")
person := Person{
Name: "Wang Wu",
Age: 30,
Birth: birth,
Children: []string{},
}
jsonBytes, _ := json.Marshal(person)
fmt.Println(string(jsonBytes)) // {}
}
使用 json:"-" 标签即可忽略所有字段, 输出的结果就是 {} 空 json,不是空字符串。
JSON 处理中精度丢失的问题
反序列化时明确指定了明确的结构体和类型
type User struct {
Name string
FansCount int64
}
// 如果反序列化的时候指定明确的结构体和变量类型
func TestJsonUnmarshal(t *testing.T) {
const jsonStream = `
{"name":"ethancai", "fansCount": 9223372036854775807}
`
var user User // 类型为User
err := JsonUnmarshal(jsonStream, &user)
if err != nil {
fmt.Println("error:", err)
}
fmt.Printf("%+v \n", user)
}
运行结果:
=== RUN TestJsonUnmarshal
{Name:ethancai FansCount:9223372036854775807}
--- PASS: TestJsonUnmarshal (0.00s)
PASS
反序列化时不指定结构体类型或者变量,JSON 数据类型,默认是 float64
func TestJsonMarshal(t *testing.T) {
const jsonStream = `
{"name":"ethancai", "fansCount": 9223372036854775807}
`
var user interface{} // 类型为User
err := JsonUnmarshal(jsonStream, &user)
if err != nil {
fmt.Println("error:", err)
}
m := user.(map[string]interface{})
fansCount := m["fansCount"]
fmt.Printf("%+v \n", reflect.TypeOf(fansCount).Name())
fmt.Printf("%+v \n", fansCount.(float64))
}
运行结果:
=== RUN TestJsonMarshal
float64
9.223372036854776e+18
--- PASS: TestJsonMarshal (0.00s)
PASS
可以看到,如果数据精度比较高的时候,反序列化成 float64 类型的数值时存在精度丢失的问题。
JSON 时间格式日期处理问题
func TestTimeProcess(t *testing.T) {
type Product struct {
Name string
CreatedAt time.Time
}
pdt := Product{
Name: "Reds",
CreatedAt: time.Now(),
}
b := JsonMarshal(pdt) //{"Name":"Reds","CreatedAt":"2021-08-20T11:03:17.698724+08:00"}
fmt.Println(b)
}
运行结果
=== RUN TestTimeProcess
{"Name":"Reds","CreatedAt":"2021-08-20T11:03:17.698724+08:00"}
--- PASS: TestTimeProcess (0.00s)
PASS
JSON的规范中并没有日期类型,不同语言的library对日期序列化的处理也不完全一致, 要特别注意。
结构体字母小写问题
如果机构题字段的首字母是小写, 解析 json 时会失败
type robot struct {
name string `json:"name"`
amount int `json:"amount"`
}
func TestPareseStruct(t *testing.T) {
fmt.Println("解析json字符串到单个结构体")
str := "{"name":"nam1","amount":100}"
one := robot{}
err := json.Unmarshal([]byte(str), &one)
if err != nil {
fmt.Printf("parse_one(), err=%v", err)
}
fmt.Printf("name=%v,amount=%v \n", one.name, one.amount)
}
运行结果
=== RUN TestPareseStruct
解析json字符串到单个结构体
name=,amount=0
--- PASS: TestPareseStruct (0.00s)
PASS
需要修改首字母为大写
type robot struct {
Name string `json:"name"`
Amount int `json:"amount"`
}
func TestPareseStruct(t *testing.T) {
fmt.Println("解析json字符串到单个结构体")
str := "{"name":"nam1","amount":100}"
one := robot{}
err := json.Unmarshal([]byte(str), &one)
if err != nil {
fmt.Printf("parse_one(), err=%v", err)
}
fmt.Printf("s=%v",one)
}
运行结果
=== RUN TestPareseStruct
解析json字符串到单个结构体
s={nam1 100}--- PASS: TestPareseStruct (0.00s)
PASS