原文链接

背景

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

只在反序列化时拆分[1:1][2:1]

缺点是 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)
}

参考


  1. Golang 中使用 JSON 的一些小技巧

  2. JSON and struct composition in Go

Golang 通过创建临时结构体实现 struct 内 interface struct 的 json 反序列化的更多相关文章

  1. 换个语言学一下 Golang (9)——结构体和接口

    基本上到这里的时候,就是上了一个台阶了.Go的精华特点即将展开. 结构体定义 上面我们说过Go的指针和C的不同,结构体也是一样的.Go是一门删繁就简的语言,一切令人困惑的特性都必须去掉. 简单来讲,G ...

  2. Golang通过反射获取结构体的标签

    Golang通过反射获取结构体的标签 例子: package main import ( "fmt" "reflect" ) type resume struc ...

  3. C# 结构体和List<T>类型数据转Json数据保存和读取

    C#  结构体和List<T>类型数据转Json数据保存和读取 一.结构体转Json public struct FaceLibrary { public string face_name ...

  4. GO学习-(38) Go语言结构体转map[string]interface{}的若干方法

    结构体转map[string]interface{}的若干方法 本文介绍了Go语言中将结构体转成map[string]interface{}时你需要了解的"坑",也有你需要知道的若 ...

  5. golang 使用reflect反射结构体

    "反射结构体"是指在程序执行时,遍历结构体中的字段以及方法. 1.反射结构体 下面使用一个简单的例子说明如何反射结构体. 定义一个结构体,包括3个字段,以及一个方法. 通过refl ...

  6. golang 修改数组中结构体对象的值的坑

    对对象数组逐个修改元素属性时候没有成功,代码如下: for _, configure := range configures { configure.Price = specPriceMap[conf ...

  7. 结构体指针 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 ...

  8. Golang 入门 : 结构体(struct)

    Go 通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型.试图表示一个现实世界中的实体. 结构体由一系列命名的元素组成,这些元素又被称为字段,每个字段都有一个名称和 ...

  9. 将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. ...

随机推荐

  1. libevent源码学习(13):事件主循环event_base_loop

    目录开启事件主循环执行事件主循环校对时间 阻塞/非阻塞处理激活队列中的event事件主循环的退出event_base_loopexitevent_base_loopbreak开启事件主循环       ...

  2. openstack 一键部署云主机

    两边虚拟机时间不一样cinder会报错. 在computer节输入命令 ntpdate 192.168.10.100 systemctl restart openstack-cinder-volume ...

  3. 使用docker logs -f 打印日志中文无法正常显示

    打印docker日志的时候 中文无法显示 显示了问号 在Dockerfile加入 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US ...

  4. Linux(centos)系统导出数据库文件命令

    mysqldump -uroot -p test > /test.sql -uroot 其中的root是数据库的用户名 test是要导出的数据库名字 test.sql 是要导出的数据库文件名字, ...

  5. Redis报错:RDB snapshots, but it is currently not able to persist on disk 处理

    Response received : -MISCONF Redis is configured to save RDB snapshots, but it is currently not able ...

  6. TCP/IP协议竟然有这么多漏洞

    网络攻击是指利用网络存在的漏洞和安全缺陷对网络系统的软硬件及其系统数据进行攻击的行为.TCP/IP协议作为网络的基础协议,从设计之初并没有考虑到网络将会面临如此多的威胁,导致出现了许多攻击方法.由于网 ...

  7. Git从远程仓库克隆

    首先,登陆GitHub,创建一个新的仓库,名字叫gitskills 勾选Initialize this repository with a README,这样GitHub会自动为我们创建一个READM ...

  8. FreeSql.Provider.SqliteCore如何加密

    FreeSql.Provider.SqliteCore 是FreeSql基于微软提供的最新的Microsoft.Data.Sqlite.Core驱动的实现. 支持的版本 .NETStandard2.0 ...

  9. linux中网络存储与考试系统搭建(实现多用户可以共享文件)

    上期内容回顾 1.数据备份的方式有哪些 全量和增量 2.数据备份的命令有哪些 都有哪些优点缺点 cp : 本地复制,全量复制 scp : 远程复制,全量复制 rsync : 远程复制,增量复制 3.r ...

  10. ssm项目使用过滤器出现4040错误

    目录 问题 解决方法 (1)方法一 (2)方法二 问题 过滤器处理乱码问题 public class CharFilter implements Filter { @Override public v ...