Go语言实现简单的反序列化

news/2024/9/18 3:42:23/文章来源:https://www.cnblogs.com/ffff5/p/18409387

这段代码实现了一个简单的 JSON 序列化和反序列化功能。代码包括一个 User 结构体和两个函数 MarshalUnMarshal,分别用于将数据转换为 JSON 格式和将 JSON 格式的数据解析回结构体。

package mainimport ("bytes""fmt""reflect""strconv""strings"
)// User 定义了一个用户结构体,其中包含名称、年龄和性别字段
type User struct {Name stringAge  stringSex  byte `json:"gender"` // 使用 json 标签自定义字段名
}// Marshal 将任意类型 v 序列化为 JSON 格式的字节数组
func Marshal(v interface{}) ([]byte, error) {value := reflect.ValueOf(v) // 获取值的反射对象typ := value.Type() // 获取值的类型bf := bytes.Buffer{} // 使用 bytes.Buffer 以高效地构造 JSON 字符串switch typ.Kind() {case reflect.Int, reflect.Int16, reflect.Int32, reflect.Int8, reflect.Int64, reflect.Uint8:// 对于整数类型,将其转换为字符串并返回字节数组return []byte(fmt.Sprintf("%v", value.Interface())), nilcase reflect.String:// 对于字符串类型,将其加上双引号后返回字节数组return []byte("\"" + value.String() + "\""), nilcase reflect.Bool:// 对于布尔值,将其转换为 "true" 或 "false" 字符串return []byte(fmt.Sprintf("%t", value.Bool())), nilcase reflect.Float32, reflect.Float64:// 对于浮点数类型,将其转换为字符串并返回字节数组return []byte(fmt.Sprintf("%f", value.Float())), nilcase reflect.Struct:// 对于结构体类型,构造 JSON 格式的字符串bf.WriteByte('{')if value.NumField() > 0 {for i := 0; i < value.NumField(); i++ {fieldValue := value.Field(i) // 获取字段值fieldType := typ.Field(i) // 获取字段类型if fieldType.IsExported() {// 处理导出字段name := fieldType.Nameif len(fieldType.Tag.Get("json")) > 0 {// 使用 JSON 标签指定的字段名name = fieldType.Tag.Get("json")}bf.WriteByte('"')bf.WriteString(name)bf.WriteByte('"')bf.WriteByte(':')if bs, err := Marshal(fieldValue.Interface()); err == nil {bf.Write(bs)} else {return nil, err}bf.WriteByte(',')}}bf.Truncate(len(bf.Bytes()) - 1) // 删除最后一个逗号}bf.WriteByte('}')return bf.Bytes(), nildefault:// 对于不支持的类型,返回错误return nil, fmt.Errorf("暂不支持该类型%s", typ.Kind())}
}// UnMarshal 将 JSON 格式的字节数组 data 解析为类型为 v 的值
func UnMarshal(v interface{}, data []byte) error {value := reflect.ValueOf(v) // 获取值的反射对象typ := value.Type() // 获取值的类型if typ.Kind() != reflect.Ptr {return fmt.Errorf("必须为指针")} else {typ = typ.Elem() // 获取指针指向的类型value = value.Elem() // 获取指针指向的值}s := string(data) // 将字节数组转换为字符串switch typ.Kind() {case reflect.Int:// 解析整数if i, err := strconv.ParseInt(s, 10, 64); err == nil {value.SetInt(i) // 设置解析后的整数值} else {return err}case reflect.Uint8:// 解析无符号整数(字节)if i, err := strconv.ParseUint(s, 10, 64); err == nil {value.SetUint(i) // 设置解析后的字节值} else {return err}case reflect.Bool:// 解析布尔值if b, err := strconv.ParseBool(s); err == nil {value.SetBool(b) // 设置解析后的布尔值} else {return err}case reflect.Float32:// 解析浮点数if f, err := strconv.ParseFloat(s, 64); err == nil {value.SetFloat(f) // 设置解析后的浮点数值} else {return err}case reflect.String:// 解析字符串,确保字符串被双引号包围if s[0] == '"' && s[len(s)-1] == '"' {value.SetString(s[1 : len(s)-1]) // 去掉双引号} else {return fmt.Errorf("json格式不对")}case reflect.Struct:// 解析结构体if s[0] == '{' && s[len(s)-1] == '}' {arr := strings.Split(s[1:len(s)-1], ",") // 去掉大括号并拆分字段if len(arr) > 0 {fieldCount := typ.NumField()tag2Field := make(map[string]string, fieldCount)for i := 0; i < fieldCount; i++ {fieldType := typ.Field(i)name := fieldType.Nameif len(fieldType.Tag.Get("json")) > 0 {// 使用 JSON 标签指定的字段名name = fieldType.Tag.Get("json")}tag2Field[name] = fieldType.Name}for _, ele := range arr {brr := strings.SplitN(ele, ":", 2) // 拆分标签和数据tag := brr[0]if tag[0] == '"' && tag[len(tag)-1] == '"' {tag = tag[1 : len(tag)-1]if fieldName, exists := tag2Field[tag]; exists {fieldValue := value.FieldByName(fieldName)fieldType := fieldValue.Type()if fieldValue.Kind() != reflect.Ptr {fieldValue = fieldValue.Addr()if err := UnMarshal(fieldValue.Interface(), []byte(brr[1])); err != nil {return err}} else {newValue := reflect.New(fieldType.Elem())if err := UnMarshal(newValue.Interface(), []byte(brr[1])); err != nil {return err} else {fieldValue.Set(newValue)}}}} else {return fmt.Errorf("json格式不对%s", tag)}}}} else {return fmt.Errorf("格式错误")}default:// 对于不支持的类型,返回错误return fmt.Errorf("暂不支持该数据类型%s", typ.Kind())}return nil
}// main 函数演示了如何使用 Marshal 和 UnMarshal 函数
func main() {user := User{Name: "ffff5",Age:  "35",Sex:  1,}if date, err := Marshal(user); err == nil {fmt.Println(string(date)) // 打印序列化后的 JSON 字符串var u2 = userif err := UnMarshal(&u2, date); err == nil {fmt.Println(u2.Name, u2.Age, u2.Sex) // 打印反序列化后的数据} else {fmt.Println(err) // 打印错误信息}} else {fmt.Println(err) // 打印序列化错误}
}
  • User 结构体:

    • 定义了一个包含 Name(名称)、Age(年龄)和 Sex(性别)字段的结构体。
    • Sex 字段使用了 json 标签来指定 JSON 中的字段名为 "gender"
  • Marshal 函数:

    • 将传入的任意类型的值转换为 JSON 格式的字节数组。
    • 支持整数、字符串、布尔值、浮点数和结构体。
    • 对于结构体,处理字段标签和序列化字段值。
  • UnMarshal 函数:

    • 将 JSON 格式的字节数组解析为传入指针类型 v 所指向的值。
    • 支持整数、字节(无符号 8 位整数)、布尔值、浮点数、字符串和结构体。
    • 处理 JSON 格式,支持通过标签来解析结构体字段。
  • main 函数:

    • 创建一个 User 对象,并将其序列化为 JSON。
    • 将序列化后的 JSON 字符串反序列化为新的 User 对象,并打印结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/795830.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

vue3 setup语法糖 扩展

安装扩展 npm i vite-plugin-vue-setup-extend 修改配置文件接下啦就可以直接在标签中写name了

ISCC 2024 部分WP

练武题 WEB 还没想好名字的塔防游戏 题目中给了塔防游戏的github原项目地址。下载题目的网页源代码,和github项目对比,发现基本只加了world.js里的三个提示。Cats Craft Scarves Ivory Towers Twinkle Dragons Whisper Secrets提示不知道是什么意思。但是看首字母有点奇怪,另…

protobuf cmake Visual Studio 编译安装 (全命令行)

protobuf cmake Visual Studio 编译安装 (全命令行)protobuf cmake Visual Studio 编译安装 中间踩了挺多的坑的, 这篇文章记录一下. 重要前言: 所有在引用框中的命令都不要输入!!cmake --install . # 在引用框中的不要输入到命令行cmake --install . --config Debug # 命令…

陈彦吉的第一次作业

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/rjjc这个作业的目标 向教师和助教介绍自己,阐述自己期望的课程收获和扮演的课程实践角色姓名-学号 陈彦吉 2022329301139一、自我介绍 (一)基本信息 我叫陈彦吉,来自浙江台州,是2022级电气工程及其自动化(2)班…

(16)USB通信

USB协议讲解(大范围讲解) USB,英文全称 Universal Serial Bus(通用串行总线),是一种支持热插拔的高速串行传输总线(目前已发展至3.0) USB体系包括主机、设备以及物理连接三部分,其中: 主机是一个提供USB接口以及接口管理能力的硬件、软件及固件复合体,可以使PC,也可…

Redis 入门 - C#|.NET Core客户端库六种选择

介绍了6款.NET系Redis客户端库:ServiceStack.Redis、StackExchange.Redis、CSRedisCore、FreeRedis、NewLife.Redis、BeetleX.Redis,各具特色,如商业支持、高性能、高并发、低延迟等,适合不同场景和需求。经过前面的Redis基础学习,今天正式进入编码阶段了,进入编码阶段我…

[NLP] 知识抽取技术

1 概述:知识抽取 定义知识抽取通常指从非结构化文本中挖掘结构化信息。例如,含有丰富语义信息的标签和短语。 这在业界被广泛应用于内容理解和商品理解等场景,通过从用户生成的文本信息中提取有价值的标签,将其应用于内容或商品上知识抽取通常伴随着对所抽取标签或短语的分…

如何用 Scrapy 爬取网站数据并在 Easysearch 中进行存储检索分析

做过数据分析和爬虫程序的小伙伴想必对 Scrapy 这个爬虫框架已经很熟悉了。今天给大家介绍下,如何基于 Scrapy 快速编写一个爬虫程序并利用 Easysearch 储存、检索、分析爬取的数据。我们以极限科技的官网 Blog 为数据源,做下实操演示。 安装 scrapy 使用 Scrapy 可以快速构建…

个人项目—论文查重

个人项目———论文查重这个作业属于哪个课程 计科12班这个作业的要求在哪里 作业要求这个作业的目标 实现论文查重,给定原文件和抄袭的文件,输出二者的相似度到答案文件中GitHub链接:github 1.题目要求 题目:论文查重 描述如下: 设计一个论文查重算法,给出一个原文文件和…

一款用于Redis漏洞的利用工具

Hack分享吧声明 该公众号分享的安全工具和项目均来源于网络,仅供安全研究与学习之用,如用于其他用途,由使用者承担全部法律及连带责任,与工具作者和本公众号无关。工具介绍 @yuyan-sec使用golang写的一款Redis漏洞利用工具。 注意:主从复制会清空数据,主从复制会清空数据…

AGC007F Shik and Copying String

涉及知识点:Ad-hoc,贪心 题意 Link 给出两个长度相同的字符串 \(S,T\),定义一次操作为: 从头至尾处理每一位,每位可以变成上一位,或者不变。 求最少对 \(S\) 进行多少次操作使得 \(S=T\)。 思路 引理 可以发现,一次操作其实类似于选择一些点 \(l\),从左到右覆盖它后面的…

全网最适合入门的面向对象编程教程:47 Python函数方法与接口-回调函数Callback

回调函数是编程中一种非常常见的模式,用于将函数作为参数传递给其他函数或方法。这种模式在 Python 中广泛应用于事件处理、异步编程、函数式编程等场景。全网最适合入门的面向对象编程教程:47 Python 函数方法与接口-回调函数 Callback摘要: 回调函数是编程中一种非常常见的…