Golang 通过创建临时结构体实现 struct 内 interface struct 的 json 反序列化
背景
type AData struct {
A string `json:"a"`
}
type BData struct {
B string `json:"b"`
}
type Message struct {
Name string `json:"name"`
Id int `json:"id"`
Data interface{} `json:"data"`
}
对于 interface 类型的数据很容易实现序列化(不需要任何额外步骤)
msgA := Message{
Name: "msg_a",
Id: 1,
Data: AData{
A: "a_data",
},
}
msgB := Message{
Name: "msg_b",
Id: 2,
Data: BData{
B: "b_data",
},
}
msgAJ, _ := json.Marshal(msgA)
log.Info("A", zap.Reflect("msgA", msgA), zap.ByteString("msgAJ", msgAJ))
msgBJ, _ := json.Marshal(msgB)
log.Info("B", zap.Reflect("msgB", msgB), zap.ByteString("msgBJ", msgBJ))
但 interface 反序列化后会变成 map[string]interface 类型,想要转成 struct 只能使用 mapstructure 之类的库
var msgX Message
_ = json.Unmarshal(msgAJ, &msgX)
log.Info("X", zap.Reflect("msgX", msgX), zap.Reflect("msgX.Data.A", msgX.Data.(AData).A))
// panic: interface conversion: interface {} is map[string]interface {}, not main.AData
此处是无法直接用 msgX.Data.A 来访问的,同样的 msgX.Data.(AData).A 也是不行的,因为这时候的 data 已经被反序列化成了 map[string]interface
解决方法 1
解决方法也很简单,只要再反序列化时能够知道需要反序列化成的类型即可。
在解析的时候定义临时 struct 继承 Message 并重新定义 Data 的类型。
msgXA := struct {
*Message
Data AData `json:"data"`
}{}
_ = json.Unmarshal(msgAJ, &msgXA)
log.Info("XA", zap.Reflect("msgXA", msgXA), zap.Reflect("msgXA.Data.A", msgXA.Data.A))
msgXB := struct {
*Message
Data BData `json:"data"`
}{}
_ = json.Unmarshal(msgBJ, &msgXB)
log.Info("XB", zap.Reflect("msgXB", msgXB), zap.Reflect("msgXB.Data.B", msgXB.Data.B))
解决方法 2
另一种思路是拆分 struct 每次序列化时将其合并,反序列化时再将其拆分[1][2]
缺点是每次需要传送两个变量
type ShortMessage struct {
Name string `json:"name"`
Id int `json:"id"`
}
func TestJsonStructSplit(t *testing.T) {
msgA := ShortMessage{
Name: "msg_a",
Id: 1,
}
dataA := AData{
A: "a_data",
}
msgB := ShortMessage{
Name: "msg_b",
Id: 2,
}
dataB := BData{
B: "b_data",
}
// marshal
msgAJ, _ := json.Marshal(struct {
*ShortMessage
*AData
}{&msgA, &dataA})
msgBJ, _ := json.Marshal(struct {
*ShortMessage
*BData
}{&msgB, &dataB})
// unmarshal
var msgXA ShortMessage
var dataXA AData
_ = json.Unmarshal(msgAJ, &struct {
*ShortMessage
*AData
}{&msgXA, &dataXA})
var msgXB ShortMessage
var dataXB BData
_ = json.Unmarshal(msgBJ, &struct {
*ShortMessage
*BData
}{&msgXB, &dataXB})
}
解决方法 3
缺点是 Data 实际上被解析了两次(一次解析成了 map,另一次解析成了 struct),而且每次使用时候还必须进行类型转换
var msgXA Message
var dataXA AData
_ = json.Unmarshal(msgAJ, &struct {
*Message
*AData `json:"data"`
}{&msgXA, &dataXA})
msgXA.Data = dataXA
t.Log("msgXA", msgXA, "data", msgXA.Data.(AData).A)
var msgXB Message
var dataXB BData
_ = json.Unmarshal(msgBJ, &struct {
*Message
*BData `json:"data"`
}{&msgXB, &dataXB})
msgXB.Data = dataXB
t.Log("msgXB", msgXB, "data", msgXB.Data.(BData).B)
完整测试代码
package main
import (
"encoding/json"
"testing"
)
type AData struct {
A string `json:"a"`
}
type BData struct {
B string `json:"b"`
}
type Message struct {
Name string `json:"name"`
Id int `json:"id"`
Data interface{} `json:"data"`
}
var msgA = Message{
Name: "msg_a",
Id: 1,
Data: AData{
A: "a_data",
},
}
var msgB = Message{
Name: "msg_b",
Id: 2,
Data: BData{
B: "b_data",
},
}
func TestJsonStruct(t *testing.T) {
// marshal
msgAJ, _ := json.Marshal(msgA)
msgBJ, _ := json.Marshal(msgB)
// unmarshal
msgXA := struct {
*Message
Data AData `json:"data"`
}{}
_ = json.Unmarshal(msgAJ, &msgXA)
t.Log("msgXA", msgXA, "data", msgXA.Data.A)
msgXB := struct {
*Message
Data BData `json:"data"`
}{}
_ = json.Unmarshal(msgBJ, &msgXB)
t.Log("msgXB", msgXB, "data", msgXB.Data.B)
}
type ShortMessage struct {
Name string `json:"name"`
Id int `json:"id"`
}
var msgAS = ShortMessage{
Name: "msg_as",
Id: 1,
}
var dataA = AData{
A: "a_data",
}
var msgBS = ShortMessage{
Name: "msg_bs",
Id: 2,
}
var dataB = BData{
B: "b_data",
}
func TestJsonStructSplit(t *testing.T) {
// marshal
msgAJ, _ := json.Marshal(struct {
*ShortMessage
*AData
}{&msgAS, &dataA})
msgBJ, _ := json.Marshal(struct {
*ShortMessage
*BData
}{&msgBS, &dataB})
// unmarshal
var msgXA ShortMessage
var dataXA AData
_ = json.Unmarshal(msgAJ, &struct {
*ShortMessage
*AData
}{&msgXA, &dataXA})
t.Log("msgXA", msgXA, "data", dataXA.A)
var msgXB ShortMessage
var dataXB BData
_ = json.Unmarshal(msgBJ, &struct {
*ShortMessage
*BData
}{&msgXB, &dataXB})
t.Log("msgXB", msgXB, "data", dataXB.B)
}
func TestJsonStructFull(t *testing.T) {
// marshal
msgAJ, _ := json.Marshal(msgA)
msgBJ, _ := json.Marshal(msgB)
// unmarshal
var msgXA Message
var dataXA AData
_ = json.Unmarshal(msgAJ, &struct {
*Message
*AData `json:"data"`
}{&msgXA, &dataXA})
msgXA.Data = dataXA
t.Log("msgXA", msgXA, "data", msgXA.Data.(AData).A)
var msgXB Message
var dataXB BData
_ = json.Unmarshal(msgBJ, &struct {
*Message
*BData `json:"data"`
}{&msgXB, &dataXB})
msgXB.Data = dataXB
t.Log("msgXB", msgXB, "data", msgXB.Data.(BData).B)
}
参考
Golang 通过创建临时结构体实现 struct 内 interface struct 的 json 反序列化的更多相关文章
- 换个语言学一下 Golang (9)——结构体和接口
基本上到这里的时候,就是上了一个台阶了.Go的精华特点即将展开. 结构体定义 上面我们说过Go的指针和C的不同,结构体也是一样的.Go是一门删繁就简的语言,一切令人困惑的特性都必须去掉. 简单来讲,G ...
- Golang通过反射获取结构体的标签
Golang通过反射获取结构体的标签 例子: package main import ( "fmt" "reflect" ) type resume struc ...
- C# 结构体和List<T>类型数据转Json数据保存和读取
C# 结构体和List<T>类型数据转Json数据保存和读取 一.结构体转Json public struct FaceLibrary { public string face_name ...
- GO学习-(38) Go语言结构体转map[string]interface{}的若干方法
结构体转map[string]interface{}的若干方法 本文介绍了Go语言中将结构体转成map[string]interface{}时你需要了解的"坑",也有你需要知道的若 ...
- golang 使用reflect反射结构体
"反射结构体"是指在程序执行时,遍历结构体中的字段以及方法. 1.反射结构体 下面使用一个简单的例子说明如何反射结构体. 定义一个结构体,包括3个字段,以及一个方法. 通过refl ...
- golang 修改数组中结构体对象的值的坑
对对象数组逐个修改元素属性时候没有成功,代码如下: for _, configure := range configures { configure.Price = specPriceMap[conf ...
- 结构体指针 Pointers to Structures struct Books Book1; struct Books *struct_pointer;
小结: 1.To access the members of a structure using a pointer to that structure, you must use the → ope ...
- Golang 入门 : 结构体(struct)
Go 通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型.试图表示一个现实世界中的实体. 结构体由一系列命名的元素组成,这些元素又被称为字段,每个字段都有一个名称和 ...
- 将c语言的结构体定义变成对应的golang语言的结构体定义,并将golang语言结构体变量的指针传递给c语言,cast C struct to Go struct
https://groups.google.com/forum/#!topic/golang-nuts/JkvR4dQy9t4 https://golang.org/misc/cgo/gmp/gmp. ...
随机推荐
- java 数据类型:集合接口Collection之List~ArrayList:remove移除;replaceAll改变原有值;sort排序;迭代器listIterator();
什么是List集合: 特点: 元素有序可重复的集合. 集合中每个元素都有其对应的顺序索引. List集合默认按元素的添加顺序设置元素的索引,索引从0开始. List接口的常用方法: List可以使 ...
- mongodb 连接方式之mongo-shell
单机连接:mongo 120.131.0.64:27017 -u root -p KingSoft@1314 --authenticationDatabase admin 字符串连接 python i ...
- mobx在react的使用
创建项目第六步 mobx 1.安装 yarn add mobx yarn add mobx-react 2.新建/src/store/store.js import {observable, co ...
- mybatis注解版in查询、字符串判空模糊匹配 、批量插入、插入返回主键
IN查询 @Select({"<script> " + " select * "+ " from business_threat bt \ ...
- 总结Vue第三天:模块化和webpack模块化打包:
总结Vue第三天:模块化和webpack模块化打包: 一.❀ 模块化 [导入import-----导出export] 1.为什么需要模块化? JavaScript 发展初期,代码简单地堆积在一起,只要 ...
- 【LeetCode】129. Sum Root to Leaf Numbers 解题报告(Python)
[LeetCode]129. Sum Root to Leaf Numbers 解题报告(Python) 标签(空格分隔): LeetCode 题目地址:https://leetcode.com/pr ...
- 快速登陆linux服务器
前言 本文适用于喜欢原生终端的用户,钟爱第三方ssh客户端的可以无视....客户端可以保存用户信息和密码,比较无脑.mac可以使用终端,win可以使用git的bash. 上次分享了配置非对称秘钥免密登 ...
- TensorFlow.NET机器学习入门【7】采用卷积神经网络(CNN)处理Fashion-MNIST
本文将介绍如何采用卷积神经网络(CNN)来处理Fashion-MNIST数据集. 程序流程如下: 1.准备样本数据 2.构建卷积神经网络模型 3.网络学习(训练) 4.消费.测试 除了网络模型的构建, ...
- 使用.NET 6开发TodoList应用(12)——实现ActionFilter
系列导航及源代码 使用.NET 6开发TodoList应用文章索引 需求 Filter在.NET Web API项目开发中也是很重要的一个概念,它运行在执行MVC响应的Pipeline中执行,允许我们 ...
- JVM垃圾收集器专题
垃圾收集器:利用垃圾收集算法,实现垃圾回收的实践落地. 1 HotSpot垃圾回收器 HotSpot垃圾回收器有多个,可以配合使用. 1.1 垃圾回收的一些术语 术语: Stop the world ...