json 反序列化 父子类型_Golang处理JSON(二) 反序列化

c46d311a219b1892f4d7bfa140206154.png

在Go语言中,encoding/json标准包处理json数据的序列化与反序列化问题。如果想了解序列化可以看这一篇序列化。与编码json的Marshal类似,解析json也提供了Unmarshal方法。对于解析json,也大致分两步,首先定义结构,然后调用Unmarshal方法序列化。

反序列化 Unmarshal()

反序列化源码放在:

48a6e817ca198bd8ddd4df2915ea126e.png

Unmarshal

// Unmarshal parses the JSON-encoded data and stores the result
// in the value pointed to by v. If v is nil or not a pointer,
// Unmarshal returns an InvalidUnmarshalError.
//
// Unmarshal uses the inverse of the encodings that
// Marshal uses, allocating maps, slices, and pointers as necessary,
// with the following additional rules:
//
// To unmarshal JSON into a pointer, Unmarshal first handles the case of
// the JSON being the JSON literal null. In that case, Unmarshal sets
// the pointer to nil. Otherwise, Unmarshal unmarshals the JSON into
// the value pointed at by the pointer. If the pointer is nil, Unmarshal
// allocates a new value for it to point to.
//
// To unmarshal JSON into a value implementing the Unmarshaler interface,
// Unmarshal calls that value's UnmarshalJSON method, including
// when the input is a JSON null.
// Otherwise, if the value implements encoding.TextUnmarshaler
// and the input is a JSON quoted string, Unmarshal calls that value's
// UnmarshalText method with the unquoted form of the string.
//
// To unmarshal JSON into a struct, Unmarshal matches incoming object
// keys to the keys used by Marshal (either the struct field name or its tag),
// preferring an exact match but also accepting a case-insensitive match. By
// default, object keys which don't have a corresponding struct field are
// ignored (see Decoder.DisallowUnknownFields for an alternative).
//
// To unmarshal JSON into an interface value,
// Unmarshal stores one of these in the interface value:
//
//	bool, for JSON booleans
//	float64, for JSON numbers
//	string, for JSON strings
//	[]interface{}, for JSON arrays
//	map[string]interface{}, for JSON objects
//	nil for JSON null
//
// To unmarshal a JSON array into a slice, Unmarshal resets the slice length
// to zero and then appends each element to the slice.
// As a special case, to unmarshal an empty JSON array into a slice,
// Unmarshal replaces the slice with a new empty slice.
//
// To unmarshal a JSON array into a Go array, Unmarshal decodes
// JSON array elements into corresponding Go array elements.
// If the Go array is smaller than the JSON array,
// the additional JSON array elements are discarded.
// If the JSON array is smaller than the Go array,
// the additional Go array elements are set to zero values.
//
// To unmarshal a JSON object into a map, Unmarshal first establishes a map to
// use. If the map is nil, Unmarshal allocates a new map. Otherwise Unmarshal
// reuses the existing map, keeping existing entries. Unmarshal then stores
// key-value pairs from the JSON object into the map. The map's key type must
// either be a string, an integer, or implement encoding.TextUnmarshaler.
//
// If a JSON value is not appropriate for a given target type,
// or if a JSON number overflows the target type, Unmarshal
// skips that field and completes the unmarshaling as best it can.
// If no more serious errors are encountered, Unmarshal returns
// an UnmarshalTypeError describing the earliest such error. In any
// case, it's not guaranteed that all the remaining fields following
// the problematic one will be unmarshaled into the target object.
//
// The JSON null value unmarshals into an interface, map, pointer, or slice
// by setting that Go value to nil. Because null is often used in JSON to mean
// ``not present,'' unmarshaling a JSON null into any other Go type has no effect
// on the value and produces no error.
//
// When unmarshaling quoted strings, invalid UTF-8 or
// invalid UTF-16 surrogate pairs are not treated as an error.
// Instead, they are replaced by the Unicode replacement
// character U+FFFD.
//
func Unmarshal(data []byte, v interface{}) error {
	// Check for well-formedness.
	// Avoids filling out half a data structure
	// before discovering a JSON syntax error.
	var d decodeState
	err := checkValid(data, &d.scan)
	if err != nil {
		return err
	}

	d.init(data)
	return d.unmarshal(v)
}

从上面的UnMarshal()函数我们可以看到,反序列化是读取字节数组,进而解析为对应的数据结构。

注意:

  1. 不是所有的数据都可以编码为 json 格式,只有验证通过的数据结构才能被编码:
  2. json 对象只支持字符串类型的 key;要编码一个 Go map 类型,map 必须是 map[string]T(T是 json 包中支持的任何类型)
  3. channel,复杂类型和函数类型不能被编码
  4. 不支持循环数据结构;它将引起序列化进入一个无限循环
  5. 指针可以被编码,实际上是对指针指向的值进行编码(或者指针是 nil)

而在Go中,json 与 Go 类型对应如下:

  1. bool 对应 json 的 booleans
  2. float64 对应 json 的 numbers
  3. string 对应 json 的 strings
  4. nil 对应 json 的 null

在解析 json 格式数据时,若以 interface{} 接收数据,需要按照以上规则进行解析。

反序列化成struct

package main

import (
    "encoding/json"
    "fmt"
)

type Message struct {
    Address    string  `json:"address"`
    Age int  `json:23`
    Name    string `json:"name"`
}

var jsonString string = `{
    "address":"china",
    "age":23,
    "name":"程序猿编码"
}`
//将json字符串,反序列化成struct
func main() {

    message := Message{}

    err := json.Unmarshal([]byte(jsonString), &message)
    if err != nil{
		fmt.Println("反序列化失败", err)
    }
	fmt.Printf("%+vn", message)
	fmt.Println(message)
		
}

输出结果:

46e74b6a6bb28a5ea07188dc5e80a840.png

定义了1个结构体Message ,结构体Message 的Address 字段tag标签为:json:“address”,表明这个字段在json中的名字对应为address。以此类推

jsonString 我们可以认作为一个json数据,通过json.Unmarshal,我们可以把json中的数据反序列化到了对应结构体。如果定义的字段address为小写,例如:

type Message struct {
    address    string  `json:"address"`
    Age int  `json:"age"`
    Name    string `json:"name"`
}

则会输出:

47870ea480648c68c0785814fe5ae0a6.png

上面的password并不会被解析赋值json的password,大小写不敏感只是针对公有字段而言。再寻找tag或字段的时候匹配不成功,则会抛弃这个json字段的值:

type Message struct {
    Age int  `json:"age"`
    Name    string `json:"name"`
}

这是我们在定义结构体时需要注意的,字段首字母大写。

反序列化成map

直接使用 Unmarshal 把这个数据反序列化,并保存map[string]interface{} 中,要访问这个数据,我们可以使用类型断言:

package main 

import (
	"fmt"
	"encoding/json"
)


//json字符串,反序列化成map
func main(){

	str :=  "{"address":"china","age":23,"name":"minger"}"

	var data map[string]interface{}// 使用一个空接口表示可以是任意类型

	//反序列化
	err := json.Unmarshal([]byte(str),&data)
	if err !=nil{
		fmt.Printf("unmarshal err = %vn",err)
	}

	fmt.Printf("反序列化后 monster = %vn",data)

	for k, v := range data {
        switch vv := v.(type) {
        case string:
            fmt.Println(k, "是string", vv)
        case bool:
            fmt.Println(k, "是bool", vv)
        case float64:
            fmt.Println(k, "是float64", vv)
        case nil:
            fmt.Println(k, "是nil", vv)
        case []interface{}:
            fmt.Println(k, "是array:")
            for i, u := range vv {
                fmt.Println(i, u)
            }
        default:
            fmt.Println(k, "未知数据类型")
        }
    }
}

输出结果:

b456f825d98b852b36ce7f8179bcc32d.png

通过这种方式,即使是未知 json 数据结构,我们也可以反序列化,同时可以确保类型安全。

总结

golang和json的大部分数据结构匹配,对于复合结构,go语言可以借助结构体和空接口实现json的数组和对象结构。通过struct tag可以灵活的修改json编码的字段名和输出控制。

欢迎关注微信公众号【程序猿编码】,专注于Linux c/c++ 、Python、Go语言、数据结构与算法、网络编程相关知识,常用的程序员工具。还有每日00:10分之前更新 新闻简报,即刻知晓天下事!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值