go语言将struct Marshal()序列化成json,默认按照一定格式进行转换。可以实现Marshaler接口的MarshalJSON()方法,实现自定义序列化。反序列化实现Unmarshaler接口。

1、Marshal和Unmarshal使用

 type dog struct {Name stringAge  int}type person struct {Id   stringDogs []dog}dogs := []dog{dog{Name: "一",Age:  1,}}per := person{Id:   "小明",Dogs: dogs,}s, _ := json.Marshal(per)  //序列化成json格式的[]bytefmt.Println(string(s))  //输出:{"Id":"小明","Dogs":[{"Name":"一","Age":1}]}per2 := person{}_ = json.Unmarshal(s, &per2)  //将json反序列化放入结构体&per2中fmt.Println(per)    //输出:{小明 [{一 1}]}

注意:

  • json.Marshal(per)中,结构体per的字段名必须大写,小写字段名返回的[]byte是nil。
  • json.Unmarshal(s, &per2)中,&per2必须是指针且不是nil,否则err InvalidUnmarshalError。

2、序列化对应数据类型的编码

json格式有自己的数据格式规范,包括:

  • string:必须要用双引号引起来
  • number:数值,没有双引号
  • object:对象,{ key:value }表示方式,可嵌套。
  • List:数组,[ value,value ]
  • boolean:true/false,没有双引号
  • null:空值

Marshal和Unmarshal的Go语言数据类型和json格式的数据类型对应关系如下:

Go语言类型 json类型
bool boolean
string string
int,unit,float,rune number
array,slice List
struct,map object
nil null
chan,func,complex* 不支持
[]byte Base64编码的string
time.Time RFC3339格式string

[]byte和time.Time两种类型序列化较为特殊,对数据做格式编码,保存成json字符串形式。

序列化时对两种类型编码,反序列时对类型解码。编码格式如下:

[]byte:"5ZaE6Imv"

time.Time:"2022-09-05T11:04:40.3491328+08:00"

    per := person{Id: "小明",T: time.Now(),B: []byte("善良"),}s, _ := json.Marshal(per)fmt.Println(string(s))
//{"Id":"小明","T":"2022-09-05T11:04:40.3491328+08:00","B":"5ZaE6Imv"}per2 := person{}_ = json.Unmarshal(s, &per2)fmt.Println(per2)
//{小明 2022-09-05 11:04:40.3491328 +0800 CST [229 150 132 232 137 175]}fmt.Println(string(per2.B))
//善良

3、自定义序列化格式

[]byte和time.Time类型序列化是格式编码后的值,大部分场景可能不是我们希望的格式。我们使用自定义格式进行序列化。

3.1、Marshaler和Unmarshaler接口

Marshal序列化,递归遍历对象V,如果V实现了Marshaler接口,调用接口下的MarshalJSON()方法生成JSON。

// Marshaler is the interface implemented by types that
// can marshal themselves into valid JSON.
type Marshaler interface {MarshalJSON() ([]byte, error)
}

Unmarshal反序列化,如果对象V实现了Unmarshaler接口,调用接口下的UnmarshalJSON()方法获得对象V。

type Unmarshaler interface {UnmarshalJSON([]byte) error
}

3.2、实现Marshaler接口设置序列化字段格式

自定义格式的字段,将[]btye定义成新类型ByteArr,time.Time定义成新类型NewTime。方便后面实现接口。

type ByteArr []byte
type NewTime time.Timetype dogg struct {Name ByteArrTime NewTime
}

自定义格式效果:

  • []byte不使用base64编码,直接显示string形式。
  • time显示格式:2020-09-09 18:11:34

ByteArr 实现Marshaler接口和Unmarshaler接口,自定义 []byte 序列化反序列化原样显示(string格式)。

// MarshalJSON returns m as the JSON encoding of m.
func (m ByteArr) MarshalJSON() ([]byte, error) {if m == nil {return []byte("null"), nil}return m, nil
}// UnmarshalJSON sets *m to a copy of data.
func (m *ByteArr) UnmarshalJSON(data []byte) error {if m == nil {return errors.New("UnmarshalJSON on nil pointer")}*m = append((*m)[0:0], data...)return nil
}

给ByteArr实现了两个接口的方法,MarshalJSON()序列化时调用,UnmarshalJSON()反序列化时调用。两个方法内没有做Base64的编码,直接原样返回。 也可以在方法内转化成其他格式。到此自定义格式已经完成。

测试自定义 []byte 序列化

func main() {d := dogg{Name: []byte(`{"aa":"435","rt":23}`),}jsonD, err := json.Marshal(d)fmt.Println(string(jsonD), err)
//{"Name":{"aa":"435","rt":23},"Time":{}} <nil>d2 := dogg{}err = json.Unmarshal(jsonD, &d2)fmt.Println(string(d2.Name))
//{"aa":"435","rt":23}d3 := dogg{Name: []byte("会err吗"),}jsonD, err = json.Marshal(d3)
//json: error calling MarshalJSON for type main.ByteArr: invalid character 'ä'
//looking for beginning of valuefmt.Println(string(jsonD), err)d4 := dogg{}err = json.Unmarshal(jsonD, &d4)fmt.Println(string(d4.Name))
}

测试结果:d 序列化成功,json字符串为 []byte 原始数据。

d3 序列化失败。 json: error calling MarshalJSON for type main.ByteArr: invalid character 'ä' looking for beginning of value

3.3、MarshalJSON() 方法报错分析

该方法要求返回一个有效的JSON。百度搜索到的答案说:需要给方法的返回值加双引号。修改代码如下:

// MarshalJSON returns m as the JSON encoding of m.
func (m ByteArr) MarshalJSON() ([]byte, error) {s := string(m)if m == nil {//return []byte("null"), nils = "null"}st := "\"" + s + "\"" //加双引号//st := strconv.Quote(s) //加双引号和转义\return []byte(st), nil
}
//输出
//json: error calling MarshalJSON for type main.ByteArr: invalid character 'a' af
//ter top-level value//{"Name":"会err吗","Time":{}} <nil>
//"会err吗" 

只加双引号:d 序列化失败, json: error calling MarshalJSON for type main.ByteArr: invalid character 'a' after top-level value

d3 序列化成功。

加双引号并且转义\:序列化全部成功。

分析MarshalJSON()输出结果:

  • 只加双引号,d=“{"aa":"435","rt":23}”,d3="会err吗" 。
  • 加双引号并且转义\,d="{\"aa\":\"435\",\"rt\":23}",d3="会err吗"

有转义字符显然不是我们要的结果。

最终分析结果

MarshalJSON()方法的返回要求合法的JSON。合法的JSON不单单是网上说的使用双引号。

  • 如果实现接口的对象是某个字段,则返回的JSON为该字段的value。返回值符合value的JSON格式。JSON的value包括string、number、list、boolean、object等,每个数据有不同的格式,string需要双引号。number没有双引号,必须是数值。object是个JSON。list是中括号列表等等。
  • 如果实现接口是整个结构体对象,合法的JSON应该是完整的JSON对象。

因此,想要将 []byte 直接作为json的value显示,只需要满足JSON的value数据格式。

3.4、使用go语言json包中的 json.RawMessage 实现 []byte 直接json显示

json.RawMessage源码如下:

// RawMessage is a raw encoded JSON value.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte// MarshalJSON returns m as the JSON encoding of m.
func (m RawMessage) MarshalJSON() ([]byte, error) {if m == nil {return []byte("null"), nil}return m, nil
}// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {if m == nil {return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")}*m = append((*m)[0:0], data...)return nil
}

json.RawMessage使用

type dogg struct {Name json.RawMessageTime NewTime
}func main() {d := dogg{Name: []byte(`{"aa":"435","rt":23}`),}jsonD, err := json.Marshal(d)fmt.Println(string(jsonD), err)d2 := dogg{}err = json.Unmarshal(jsonD, &d2)fmt.Println(string(d2.Name))}
//{"Name":{"aa":"435","rt":23},"Time":"0001-01-01 00:10:112"} <nil>
//{"aa":"435","rt":23}

3.5、实现TextMarshaler接口自定义序列化格式

序列化时,找不到 MarshalJSON() 函数,会继续判断是否实现TextMarshaler接口。是,则调用接口下的 MarshalText() 函数。反序列化时,继续调用 TextUnmarshaler.UnmarshalText() 函数。接口如下:

// TextMarshaler is the interface implemented by an object that can
// marshal itself into a textual form.
//
// MarshalText encodes the receiver into UTF-8-encoded text and returns the result.
type TextMarshaler interface {MarshalText() (text []byte, err error)
}// TextUnmarshaler is the interface implemented by an object that can
// unmarshal a textual representation of itself.
//
// UnmarshalText must be able to decode the form generated by MarshalText.
// UnmarshalText must copy the text if it wishes to retain the text
// after returning.
type TextUnmarshaler interface {UnmarshalText(text []byte) error
}

MarshalJSON()和MarshalText()两个函数的区别:

  • MarshalJSON返回的是合法的JSON,返回的值,Marshal() 函数在拼接json字符串时直接使用。实现接口的对象是最外层结构体对象,序列化就是函数返回值。是结构体中字段,返回值是序列化的json串的value值。
  • MarshalText返回的是UTF-8的文本。序列化过程中,将函数返回值看作的json的字符串类型,Marshal() 函数会对返回值加双引号。实现接口对象是结构体字段,返回值被看作是序列化的json中value的值(字符串类型)。对象是最外层结构体,整个结构体序列化就是含双引号字符串。特殊字符默认转义

本文的数据格式:

[]byte:全部是json格式的字符串。

time.Time:普通字符串,不含特殊字符。

3.6、最终实现版本

根据数据特点,对 []byte 类型选择实现 Marshaler 接口的MarshalJSON()方法。对 time.Time 类型选择实现 TextMarshaler 接口的 MarshalText() 方法。

type ByteArr []byte
type NewTime time.Timetype dogg struct {Name ByteArrTime NewTime
}func (t NewTime) MarshalText() ([]byte, error) {tm := time.Time(t)if y := tm.Year(); y < 0 || y >= 10000 {return nil, errors.New("Time.MarshalText: year outside of range [0,9999]")}return []byte(tm.Format("2006-01-02 15:04:05")), nil
}func (t *NewTime) UnmarshalText(b []byte) error {
//必须是2006-01-02 15:04:05字符串tm, _ := time.Parse("2006-01-02 15:04:05", string(b))*t = NewTime(tm)return nil
}// MarshalJSON returns m as the JSON encoding of m.
func (m ByteArr) MarshalJSON() ([]byte, error) {if m == nil {return []byte("null"), nil}b := json.Valid(m) //判断是否是json格式(只有json的五种数据类型也合法,“a”,true,et)if !b {s := strconv.Quote(string(m)) //不是json,加双引号作为string返回return []byte(s), nil}return m, nil
}// UnmarshalJSON sets *m to a copy of data.
func (m *ByteArr) UnmarshalJSON(data []byte) error {if m == nil {return errors.New("UnmarshalJSON on nil pointer")}*m = append((*m)[0:0], data...)return nil
}func ByteMarshal() {d := dogg{Name: []byte(`{"aa":"435","rt":23}`),Time: NewTime(time.Now()),}jsonD, err := json.Marshal(d)fmt.Println(string(jsonD), err)d2 := dogg{}err = json.Unmarshal(jsonD, &d2)fmt.Println(string(d2.Name), time.Time(d.Time).Format("2006-01-02 15:04:05"))
}
//{"Name":{"aa":"435","rt":23},"Time":"2022-09-06 15:17:45"} <nil>
//{"aa":"435","rt":23} 2022-09-06 15:17:45

4、除了结构体字段实现上面两个接口,还可以让整个结构体实现这两个接口。更新。。。

Go语言Marshal序列化遇到的问题相关推荐

  1. go语言json序列化

    go语言json序列化 key-value (结构体,map,切片)序列化为json字符串操作 package mainimport ("encoding/json""f ...

  2. 包含c语言的序列化字符,Android Serializable与Parcelable原理与区别

    一.序列化.反序列化是什么? (1) 名词解释 对象的序列化 : 把Java对象转换为字节序列并存储至一个储存媒介的过程. 对象的反序列化:把字节序列恢复为Java对象的过程. (2) 序列化详细解释 ...

  3. java marshal 序列化_marshal 对象的序列化

    有时候,要把内存中的一个对象持久化保存到磁盘上,或者序列化成二进制流通过网络发送到远程主机上.Python中有很多模块提供了序列化与反序列化的功能,如:marshal, pickle, cPickle ...

  4. Go语言 ProtoBuf 序列化和反序列化

    前言 Protobuf 是Google开发的一个网络通信协议,提供了高效率的序列化和反序列化机制,序列化就是把对象转换成二进制数据发送给服务端,反序列化就是将收到的二进制数据转换成对应的对象.官方版本 ...

  5. c 语言 json序列化,C#中json字符串的序列化和反序列化 – 万能的聪哥 – 博客园...

    今日写番茄闹钟程序,打算添加日程安排内容,使用到json格式文件的序列化和反序列化: 什么是Json ? Json[ 它是一个轻量级的数据交换格式,我们可以很简单的来读取和写它, 并且它很容易被计算机 ...

  6. json序列化c语言,C语言JSON序列化/反序列化

    最近想找一个C语言处理嵌套结构体和结构体数组的json库,理想的是能够很容易处理复杂结构体嵌套,并且使用简单的,但是没找到比较合适的,于是打算自己封装一个: 两个问题: C语言结构体本身没有元数据,这 ...

  7. java marshal 序列化_求教Marshal类实现序列化出现的问题

    小弟最近在研究利用Marshal实现序列化和反系列化,主要是使用Marshal类里的两个方法,StructureToPtr和PtrToStructure,这两个类的具体讲解和用法我就不多赘述 了,具体 ...

  8. c语言xml序列化,C# XML和实体类之间相互转换(序列化和反序列化)

    我们需要在XML与实体类,DataTable,List之间进行转换,下面是XmlUtil类,该类来自网络并稍加修改. using System; using System.Collections.Ge ...

  9. c语言 文件序列化,Obj-C中的NSDate 、文件读写、序列化和反序列化

    NSData *data = [NSData data]; //获取当前时间 NSLog ("Today is %@", data); NSArray *phrase; phras ...

最新文章

  1. 测试数据精准对比的思考
  2. 在Spark中自定义Kryo序列化输入输出API(转)
  3. wso2 安装_WSO2注册表安装简介
  4. 在 Apex 代码中生成 csv 文件
  5. sklearn分类任务模型评价指标汇总与AUC-ROC曲线及代码示例
  6. Bug:Google Analytics例子未使用example.com
  7. Gerrit搭建与代码下载
  8. 基于tcp实现远程执行命令
  9. 关于AngularJS:
  10. pythonATM,购物车项目实战_补充7-start.py
  11. 深度linux使用入门教程,Linux初级使用指南
  12. 【H5调用iOS原生高德定位】
  13. 我的飞信发展方案(一)
  14. Matlab信号处理,小波降噪
  15. 学计算机基础的重要性,浅谈学习计算机基础知识的重要性.pdf
  16. 计算机连接未识别的网络,电脑网络连接出现未识别的网络怎么办
  17. eog - eog:19220): GLib-GIO-ERROR **: Settings schema ‘org.gnome.eog.plugins‘ is not installed
  18. 密立根油滴实验实验数据处理
  19. JDK源码解析 - java.util.ArrayList
  20. 原创丨嵌入式IMX6ULL学习指引-四大要点-简单也实用

热门文章

  1. 如何很好的看很大的思维导图
  2. 常见的 shell 命令整理
  3. 最大扇入数怎么判断_绝地求生:被扔雷怎么办?青铜:被炸,星钻:赶紧跑开,王牌:我是教科书...
  4. 使用Winform写的一个LOL自动登录(含网盘) 自动输入账号密码
  5. python - 极坐标 + 散点图
  6. 群晖之frp内网穿透
  7. PAT 秀恩爱分的快
  8. 11 wifi6速率_走进WiFi6
  9. 28岁,他是如何成为上市公司测试总监的
  10. 连接不上虚拟机的mysql