基于Golang的对象序列化的程序包开发——myJsonMarshal
基于Golang的对象序列化的程序包开发——myJsonMarshal【阅读时间:约10分钟】
- 一、对象序列化概述
- 二、系统环境&项目介绍
- 1.系统环境
- 2.项目的任务要求
- 三、具体程序设计及Golang代码实现
- 1.程序设计
- 2.JsonMarshal函数
- 3.Marshal函数
- 4.StringTrans函数
- 5.StructTrans函数
- 6.MapTrans函数
- 7.SliceTrans函数
- 8.ArrayTrans函数
- 9.PtrTrans函数
- 四、程序测试
- 1.封装并使用程序包
- 2.功能测试
- 3.单元测试
- 五、中文 api 文档
- 六、完整代码
- 七、References
一、对象序列化概述
将一个对象写成特定文本格式的字符流,称为序列化。在本次开发的程序包myJsonMarshal
中,我们可以利用它将将结构数据格式化为 json 字符流。
二、系统环境&项目介绍
1.系统环境
操作系统:CentOS7
硬件信息:使用virtual box配置虚拟机(内存3G、磁盘30G)
编程语言:GO 1.15.2
2.项目的任务要求
- 参考官方 encoding/json 包 Marshal 函数,将结构数据格式化为 json 字符流
- 必须导出
func JsonMarshal(v interface{}) ([]byte, error)
- 可以参考、甚至复制原来的代码
- 支持字段的标签(Tag),标签满足
mytag:"你自己的定义"
- 不允许使用第三方包
- 必须导出
- 包必须包括以下内容:
- 生成的中文 api 文档
- 有较好的 Readme 文件,包括一个简单的使用案例
- 每个go文件必须有对应的测试文件
三、具体程序设计及Golang代码实现
1.程序设计
本次程序包开发实现较为简单,只要根据输入的结构数据的类型,逐步分为多种基础的类型,如int、string和array等,然后将输入数据及其类型转为byte[],再转为json格式的字符流即可。
其中,参考官方 encoding/json 包 Marshal 函数,在判断数据类型时用到了reflect
程序包。
myJsonMarshal
程序包的函数架构如下:
下面按照myJsonMarshal
程序包的源码顺序来依次介绍数据结构和相关函数。
2.JsonMarshal函数
//JsonMarshal 输入结构化数据,返回json字符流和error
func JsonMarshal(v interface{}) ([]byte, error) {b, err := Marshal(v)if err != nil {return nil, err}return b, nil
}
JsonMarshal函数是本程序包的主要接口,通过输入结构化数据,可以返回json字符流和报错信息error。
3.Marshal函数
//Marshal 将接口数据类型的数据转为json字符流
func Marshal(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)typeJson := json.Type()switch typeJson.Kind() {case reflect.Invalid:return []byte("Invalid"), errors.New("Invalid")case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32,reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32,reflect.Uint64, reflect.Float32, reflect.Float64:return []byte(fmt.Sprintf("%v", json.Interface())), nilcase reflect.String:return StringTrans(v)case reflect.Struct:return StructTrans(v)case reflect.Map:return MapTrans(v)case reflect.Slice:return SliceTrans(v)case reflect.Array:return ArrayTrans(v)case reflect.Ptr:return PtrTrans(v)default:return []byte("unsupportedTypeTrans"), errors.New("unsupportedTypeTrans")}
}
Marshal函数调用reflect
程序包,通过switch-case语句,将不同接口数据类型的数据转为json字符流。
4.StringTrans函数
//StringTrans 将string数据转为json字符流
func StringTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)return []byte("\"" + json.String() + "\""), nil
}
StringTrans函数将string数据转为json字符流
5.StructTrans函数
//StructTrans 将struct数据转为json字符流
func StructTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)typeJson := json.Type()b := "{"for i := 0; i < json.NumField(); i++ {if i > 0 {b = b + ","}tag := typeJson.Field(i).Tag.Get("mytag")if tag == "" {b = b + "\"" + typeJson.Field(i).Name + "\":"} else {b = b + "\"" + tag + "\":"}tmp, err := Marshal(json.Field(i).Interface())if err != nil {return nil, err}b = b + string(tmp)}b = b + "}"return []byte(b), nil
}
StructTrans函数将struct数据转为json字符流,并且支持字段的标签(Tag),标签满足 mytag:"你自己的定义"
。如果变量打上了mytag标签,如Name旁边的 mytag:"name"
,那么转化成的json key就用该标签“name”,否则取变量名作为key。
6.MapTrans函数
//MapTrans 将map数据转为json字符流
func MapTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)i := json.MapRange()first := trueb := "{"for i.Next() {if first {first = false} else {b = b + ","}b = b + "\"" + fmt.Sprintf("\"%v\":", i.Key()) + "\":"tmp, err := Marshal(i.Value().Interface())if err != nil {return nil, err}b = b + string(tmp)}b = b + "}"return []byte(b), nil
}
MapTrans函数将map数据转为json字符流
7.SliceTrans函数
//SliceTrans 将slice数据转为json字符流
func SliceTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)b := "["for i := 0; i < json.Len(); i++ {if i > 0 {b = b + ","}tmp, err := Marshal(json.Index(i).Interface())if err != nil {return nil, err}b = b + string(tmp)}b = b + "]"return []byte(b), nil
}
SliceTrans函数将slice数据转为json字符流
8.ArrayTrans函数
//ArrayTrans 将array数据转为json字符流
func ArrayTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)b := "["for i := 0; i < json.Len(); i++ {if i > 0 {b = b + ","}tmp, err := Marshal(json.Index(i).Interface())if err != nil {return nil, err}b = b + string(tmp)}b = b + "]"return []byte(b), nil
}
ArrayTrans函数将array数据转为json字符流
9.PtrTrans函数
//PtrTrans 将ptr数据转为json字符流
func PtrTrans(v interface{}) ([]byte, error) {json := reflect.ValueOf(v)return Marshal(json.Elem().Interface())
}
StringTrans函数将ptr数据转为json字符流,指针变量编码时自动转换为它所指向的值。
四、程序测试
1.封装并使用程序包
将项目myJsonMarshal的myJsonMarshal.go文件的main函数注释掉,package改为package myJsonMarshal,然后执行如下指令:
go build
在其他路径下建立main.go,并调用myJsonMarshal.JsonMarshal函数即可。
2.功能测试
功能测试主要从用户角度测试程序包的功能,步骤如下:
创建main.go文件,内容如下(结构化数据类型及其数值可自定义):
package mainimport ("fmt""github.com/user/myJsonMarshal"
)type Stu struct {Name string `mytag:"name"`Age intHIgh boolClass *Class `mytag:"class"`
}type Class struct {Name stringGrade int
}func main() {//实例化一个数据结构,用于生成json字符串stu := Stu{Name: "张三",Age: 18,HIgh: true,}//指针变量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失败时err!=niljsonStu, err := myJsonMarshal.JsonMarshal(stu)if err != nil {fmt.Println("生成json字符串错误")}//jsonStu是[]byte类型,转化成string类型便于查看fmt.Println(string(jsonStu))
}
运行结果:
由此可知程序包的功能测试正常,符合的程序包的几个要求:
①结构化数据转为json字符流
②支持字段的标签(Tag),标签满足 mytag:"你自己的定义"
。
③指针变量编码时自动转换为它所指向的值
3.单元测试
单元测试主要从程序员角度,对程序包的具体函数进行测试。
建立myJsonMarshal_test.go文件,对程序包的每个函数进行单元测试如下,代码如下:
package myJsonMarshalimport ("fmt""testing"
)type Stu struct {Name string `mytag:"name"`Age intHIgh boolClass *Class `mytag:"class"`
}type Class struct {Name stringGrade int
}func Test_JsonMarshal(t *testing.T) {//实例化一个数据结构,用于生成json字符串stu := Stu{Name: "张三",Age: 18,HIgh: true,}//指针变量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失败时err!=niljsonStu, err := JsonMarshal(stu)if err != nil {fmt.Println("生成json字符串错误")}want := "{\"name\":\"张三\",\"Age\":18,\"HIgh\":true,\"class\":{\"Name\":\"1班\",\"Grade\":3}}"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_Marshal(t *testing.T) {//实例化一个数据结构,用于生成json字符串stu := Stu{Name: "张三",Age: 18,HIgh: true,}//指针变量cla := new(Class)cla.Name = "1班"cla.Grade = 3stu.Class = cla//Marshal失败时err!=niljsonStu, err := Marshal(stu)if err != nil {fmt.Println("生成json字符串错误")}want := "{\"name\":\"张三\",\"Age\":18,\"HIgh\":true,\"class\":{\"Name\":\"1班\",\"Grade\":3}}"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_StringTrans(t *testing.T) {//实例化一个数据结构,用于生成json字符串test := "unit test"//Marshal失败时err!=niljsonStu, err := StringTrans(test)if err != nil {fmt.Println("生成json字符串错误")}want := "\"unit test\""got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_StructTrans(t *testing.T) {//实例化一个数据结构,用于生成json字符串test := Class{Name: "1班",Grade: 3,}//Marshal失败时err!=niljsonStu, err := StructTrans(test)if err != nil {fmt.Println("生成json字符串错误")}want := "{\"Name\":\"1班\",\"Grade\":3}"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_MapTrans(t *testing.T) {//实例化一个数据结构,用于生成json字符串test := map[string]int{"one": 1,"two": 2,"three": 3,}//Marshal失败时err!=niljsonStu, err := MapTrans(test)if err != nil {fmt.Println("生成json字符串错误")}want := "{\"\"one\":\":1,\"\"two\":\":2,\"\"three\":\":3}"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_SliceTrans(t *testing.T) {//实例化一个数据结构,用于生成json字符串test := []int{1, 2, 3}//Marshal失败时err!=niljsonStu, err := SliceTrans(test)if err != nil {fmt.Println("生成json字符串错误")}want := "[1,2,3]"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_ArrayTrans(t *testing.T) {//实例化一个数据结构,用于生成json字符串test := [3]int{1, 2, 3}//Marshal失败时err!=niljsonStu, err := ArrayTrans(test)if err != nil {fmt.Println("生成json字符串错误")}want := "[1,2,3]"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}func Test_PtrTrans(t *testing.T) {//实例化一个数据结构,用于生成json字符串test := new(Class)test.Name = "1班"test.Grade = 3//Marshal失败时err!=niljsonStu, err := PtrTrans(test)if err != nil {fmt.Println("生成json字符串错误")}want := "{\"Name\":\"1班\",\"Grade\":3}"got := string(jsonStu)if got != want {t.Errorf("\n got %s\n want %s\n", got, want)}
}
在执行单元测试时,我们可以像上一次博客一样【博客】,在vscode中便捷地对每个函数进行test run,当编写单个函数的测试函数时,笔者更推荐这种方式。
当然,也可以像本次博客一样,在项目目录下,输入以下指令:
go test -v myJsonMarshal_test.go myJsonMarshal.go
单元测试结果如下:
[henryhzy@localhost myJsonMarshal]$ go test -v myJsonMarshal_test.go myJsonMarshal.go
=== RUN Test_JsonMarshal
--- PASS: Test_JsonMarshal (0.00s)
=== RUN Test_Marshal
--- PASS: Test_Marshal (0.00s)
=== RUN Test_StringTrans
--- PASS: Test_StringTrans (0.00s)
=== RUN Test_StructTrans
--- PASS: Test_StructTrans (0.00s)
=== RUN Test_MapTrans
--- PASS: Test_MapTrans (0.00s)
=== RUN Test_SliceTrans
--- PASS: Test_SliceTrans (0.00s)
=== RUN Test_ArrayTrans
--- PASS: Test_ArrayTrans (0.00s)
=== RUN Test_PtrTrans
--- PASS: Test_PtrTrans (0.00s)
PASS
ok command-line-arguments 0.002s
五、中文 api 文档
首先安装godoc如下:
git clone https://github.com/golang/tools $GOPATH/src/golang.org/x/tools
go build golang.org/x/tools
在项目myJsonMarshal所在目录下,执行如下指令:
go install
go doc
godoc -url="pkg/github.com/user/myJsonMarshal" > API.html
便会在当前目录下生成API.html文件:
【注意:程序包的函数名开头应为大写字母,对于函数A若含有注释,注释的开头也应为//A。】
六、完整代码
具体代码可见gitee仓库:gitee
七、References
- 课程博客
- go json.Marshal 编组函数讲解
- 官方 encoding/json 包
相关文章:

【golang程序包推荐分享】分享亿点点golang json操作及myJsonMarshal程序包开发的踩坑经历 :)
目录【阅读时间:约5分钟】一.概述1.Json的作用2.Go官方 encoding/json 包3. golang json的主要操作二.Json Marshal:将数据编码成json字符串三.Json Unmarshal:将json字符串解码到相应的数据结构四.myJsonMarshal程序包开发的踩坑经历1.go do…
Java数据结构和算法的数组
阵列的功能: 1、固定大小 2、相同的数据类型 3、 4、数据项可反复 Java数据类型:基本类型(int和double)和对象类型。在很多编程语言中。数组也是基本类型。但在Java中把它们当作对象来对待。因此在创建数组时必须使用new操作符。 …

玻璃和水晶PBR纹理材质预设
CGAxis–物理6–玻璃和水晶PBR纹理材质预设 大小:20.8G 信息: 透明玻璃、彩色玻璃和水晶的100种不同图案和变化的PBR纹理。这个包中的每个纹理由八个贴图组成:漫反射,反射,高度(位移),光泽度,环境遮挡,金属…
【POJ1113】Wall(凸包)
【题目】 Description Once upon a time there was a greedy King who ordered his chief Architect to build a wall around the Kings castle. The King was so greedy, that he would not listen to his Architects proposals to build a beautiful brick wall with a perfe…

matlab小波分析工具箱原理与应用_补充:频域特征值提取的MATLAB代码实现(小波分析)...
之前的文章信号频域分析方法的理解(频谱、能量谱、功率谱、倒频谱、小波分析)中提到了离散小波分解的例子,其参考代码如下:t_s 结果如下:原始信号离散小波分析结果左侧四行是1~4阶的近似信号,右侧四行是1~4…

【ReactiveX】基于Golang pmlpml/RxGo程序包的二次开发
基于Golang pmlpml/RxGo程序包的二次开发【阅读时间:约20分钟】一、ReactiveX & RxGo介绍1.ReactiveX2.RxGo二、系统环境&项目介绍1.系统环境2.项目的任务要求三、具体程序设计及Golang代码实现1.程序设计2.filteringOperator数据结构、op函数与newFilterOb…

python dataframe 中位数_python下的Pandas中DataFrame基本操作(一),基本函数整理
pandas作者Wes McKinney 在【PYTHON FOR DATA ANALYSIS】中对pandas的方方面面都有了一个权威简明的入门级的介绍,但在实际使用过程中,我发现书中的内容还只是冰山一角。谈到pandas数据的行更新、表合并等操作,一般用到的方法有concat、join、…

对输入框以及选择框集体的数据检验
对于一个档案输入框,有很多输入框是需要输入数据的,但有时候我们会在输入的时候遗留一些必填的项,如果不做数据校验,这时候点击保存按钮,就悲剧了,报错不说,我们前面填写的数据也就没有了。 所以…

CentOS Docker安装配置部署Golang web helloworld
目录【阅读时间:约5分钟】一、Docker简介二、Docker的安装与配置【CentOS环境】三、Docker部署Golang web helloworld四、Docker与虚拟机的区别五、吐槽一、Docker简介 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移…

[CareerCup] 2.4 Partition List 划分链表
2.4 Write code to partition a linked list around a value x, such that all nodes less than x come before all nodes greater than or equal to x. LeetCode上的原题,请参见我之前的博客Partition List 划分链表。

一条命令下载google壁纸,含错误解决方法
该命令是从google图片搜索上搜索wallpaper的大尺寸图片,匹配其中的jpg文件进行下载。 #!/bin/bash for i in {1..10}; do for url in $(wget -O- -U "" "http://images.google.com/images?imgszxxlarge&hlen&qwallpaper&saN&s…

git config —global_Git多用户配置
备注:如下的操作,Windows系统建议在Git bash工具里操作。如下操作的原理,建议查阅官方文档。Git - Reference1.取消全局设置的用户信息。Git命令:$ git config --global --unset user.name $ git config --global --unset user.em…

中级实训总结报告
目录【阅读时间:约30分钟】中级实训总结报告姓名:隐藏敏感信息 学号:隐藏敏感信息一、阶段1:项目启动1、Vi/Vim2、Java3、Ant4、Junit5、SonarQube6、 编译运行BugRunner二、阶段2:基本任务1. part2的工作(…

PHP解决方案@黑名单过滤
为什么80%的码农都做不了架构师?>>> 方案解决目标:对一些黑名单进行过滤处理 function is_spam($text, $file, $split :, $regex false){ $handle fopen($file,rb); $contents fread($handle, filesize($file)); fclose($handle); $lines …

Ubuntu 12.04下玩转终端管理器Byobu
简介 很多Linux高手都喜欢使用screen命令,screen命令可以使你轻松地使用一个终端控制其他终端。尽管screen本身是一个非常有用的工具,byobu作为screen的增强版本,比screen更加好用而且美观,并且提供有用的信息和快捷的热键。 想象…

python字典排序方法_Python字典的排序方法一则
今天需要对Python的字典进行排序,以获得有效的时间序列,采用了如下方法: 首先生成一个示例字典: >>> range_a random.sample(range(0, 10), 10) >>> range_b random.sample(range(10, 20), 10) >>> …

encodeURI 和 encodeURIComponent
保留字符 (reserved characters):这类字符是URI中的保留关键字符,它们用于分割URI中的各个部分。这些字符是:";" | "/" | "?" | ":" | "" | "&&quo…

基于Golang的简单web服务程序开发——CloudGo
基于Golang的简单web服务程序开发——CloudGo【阅读时间:约10分钟】一、概述二、系统环境&项目介绍1.系统环境2.项目的任务要求(1)基本要求(2)扩展要求三、具体程序设计及Golang代码实现1.预先准备2.CloudGoClient…

Android Studio创建项目
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010046908/article/details/47000873 创建项目 首先,先指出Android Studio中的两个概念。 Project和 Module。在Android Studio中, Project的真实含义…
Weiss的数据结构与算法分析(C++版)源码编译说明
最近尝试编译Weiss的那本数据结构与算法分析(C版)提供的源代码时,遇到一些问题,特记录如下:考虑到该书提供的代码是使用模板技术较多,这在提供简洁代码的同时,也给源码的编译带来了一些问题。因…

latex 中文_【小白向】LaTeX 中文入门
注:本文尚未撰写完毕,先暂存一下~(2020/06/27)参考学习路线[1]如何从零开始,入门 LaTeX? 孟晨 1. 卸载 CTeX 套装,安装 TeX Live原因及教程见:TeX Live 下载及安装说明2. 看完:一份其实很短的 L…

物联网兴起 嵌入式系统安全日益受关注
随着越来越多设备连接到互联网,专家们担心嵌入式系统将给企业带来严重安全风险,而很多企业还没有意识到这种风险或者无法缓解这种风险…… 随着越来越多设备连接到互联网,专家们担心嵌入式系统将给企业带来严重安全风险,而很多企业…

【Golang源码分析】Go Web常用程序包gorilla/mux的使用与源码简析
目录【阅读时间:约10分钟】一.概述二.对比: gorilla/mux与net/http DefaultServeMux三.简单使用四.源码简析1.NewRouter函数2.HandleFunc函数设置路由的HTTP方法设置路由的域名限制HTTP 方案设置路径前缀和子路由3.PathPrefix函数五.References一.概述 gorilla/mux…

FileMaker中的腳本觸發器學習筆記
脚本触发器 **脚本触发器是始终绑定到用户布局接口。对于数据表或者字段。只有在而已接口才能触发。**如果某一个布局或者对象上包含触发器,则其右下角会有触发器图标**当触发一个事件时,有且仅有一个触发器会被执行.布局级别的触发器**ONRECORDLOAD :加…

vim学习笔记(四)
下面是我的最近更新,差点删除。 下面的笔记摘自vimtutor。<CR>表示回车 删除命令 在normal模式下: de 删除一个单词,不包含空格 dw 删除一个单词,包含空格 dd 删除当前行 1,10d 删除指定行,第1到10行 ndd…

文件和缓存项目依赖
文件和缓存项目依赖 要创建缓存依赖,你需要创建一个 CacheDependency 对象并在添加依赖的缓存项目时使用它。例如,下面的代码创建一个缓存项目,它在一个 XML 文件被修改、删除、覆盖时自动从缓存中移除: CacheDependency prodDepe…

python函数的基础知识_Python入门基础知识点(函数进阶)
动态参数: 动态接收位置参数: def eat(*args): #在形参位置,*叫做聚合 print(我想吃,args) eat(大米饭,中米饭,小米饭) #收到的结果是一个tuple元祖 动态接收参数的时候要注意: 动态参数必须在位置参数后面,否则: def …

【CentOS】利用Kubeadm部署Kubernetes (K8s)
【CentOS】利用Kubeadm部署Kubernetes (K8s)【阅读时间:约10分钟】一、概述二、系统环境&项目介绍1.系统环境2.项目的任务要求三、具体实验流程1 系统准备2 安装常用包3 使用aliyun源安装docker-ce4 安装kubectl、kubelet、kubeadm5 初始…
HttpClient4.4 登录知乎(详细过程)
引言 HttpClient是java语言下一个支持http协议的客户端编程工具包,它实现了HTTP协议的所有方法,但是不支持JS渲染。我们在做一些小玩意时,有可能需要登录某些网站获取信息,那么HttpClient就是你的好帮手,废话不多说&am…

vim学习笔记(一)
:vertical sfind 垂直分隔窗口(vsf),但是两个窗口的内容完全相同。在编辑的时候,内容也完全相同,如果要关闭一个窗口,输入:exit即可:buffers 显示整个缓冲区列表ndG 删除从当前行到指定n行中的…