如何处理动态JSON in Go
假如要设计一个统计的json解析模块,json格式为
{
"type": "用来识别不同的json数据",
"msg": "嵌套的实际数据"
}
代码
package main
import (
"encoding/json"
"fmt"
"log"
)
type Envelope struct {
Type string
Msg interface{} // 接受任意的类型
}
type Sound struct {
Description string
Authority string
}
type Cowbell struct {
More bool
}
func main() {
s := Envelope{
Type: "sound",
Msg: Sound{
Description: "dynamite",
Authority: "the Bruce Dickinson",
},
}
buf, err := json.Marshal(s)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", buf)
c := Envelope{
Type: "cowbell",
Msg: Cowbell{
More: true,
},
}
buf, err = json.Marshal(c)
if err != nil {
log.Fatal(err)
}
fmt.Printf("%s\n", buf)
}
我们定义Msg类型为interface{},用来接受任意的类型。接下来试着解析msg中的字段
const input = `
{
"type": "sound",
"msg": {
"description": "dynamite",
"authority": "the Bruce Dickinson"
}
}
`
var env Envelope
if err := json.Unmarshal([]byte(input), &env); err != nil {
log.Fatal(err)
}
// for the love of Gopher DO NOT DO THIS
var desc string = env.Msg.(map[string]interface{})["description"].(string)
fmt.Println(desc)
有更好的写法,使用*json.RawMessage, 将msg字段延迟解析
type Envelope {
Type string
Msg *json.RawMessage
}
结合interface{}和*json.RawMessage的完整例子
package main
import (
"encoding/json"
"fmt"
"log"
)
const input = `
{
"type": "sound",
"msg": {
"description": "dynamite",
"authority": "the Bruce Dickinson"
}
}
`
type Envelope struct {
Type string
Msg interface{}
}
type Sound struct {
Description string
Authority string
}
func main() {
var msg json.RawMessage
env := Envelope{
Msg: &msg,
}
if err := json.Unmarshal([]byte(input), &env); err != nil {
log.Fatal(err)
}
switch env.Type {
case "sound":
var s Sound
if err := json.Unmarshal(msg, &s); err != nil {
log.Fatal(err)
}
var desc string = s.Description
fmt.Println(desc)
default:
log.Fatalf("unknown message type: %q", env.Type)
}
}
第一部分结束了,接下来还有来个地方可以提升
- 将定义的json数据中的type字段抽出来,单独定义成一个枚举常量。需要使用github.com/campoy/jsonenums
//go:generate jsonenums -type=Kind
type Kind int
const (
sound Kind = iota
cowbell
)
定义完上述内容后,执行命令
jsonenums -type=Pill
这个模块会自动生成一个*_jsonenums.go的文件,里面定义好了
func (t T) MarshalJSON() ([]byte, error)
func (t *T) UnmarshalJSON([]byte) error
这样,就帮我们把自定义的Kind和json type里的序列化和反序列化都做好了
2. 针对不同的json type字段,可以定义一个方法来返回不同的msg struct
var kindHandlers = map[Kind]func() interface{}{
sound: func() interface{} { return &SoundMsg{} },
cowbell: func() interface{} { return &CowbellMsg{} },
}
- 结合1,2把之前代码的switch块去掉
完整代码:
type App struct {
// whatever your application state is
}
// Action is something that can operate on the application.
type Action interface {
Run(app *App) error
}
type CowbellMsg struct {
// ...
}
func (m *CowbellMsg) Run(app *App) error {
// ...
}
type SoundMsg struct {
// ...
}
func (m *SoundMsg) Run(app *App) error {
// ...
}
var kindHandlers = map[Kind]func() Action{
sound: func() Action { return &SoundMsg{} },
cowbell: func() Action { return &CowbellMsg{} },
}
func main() {
app := &App{
// ...
}
// process an incoming message
var raw json.RawMessage
env := Envelope{
Msg: &raw,
}
if err := json.Unmarshal([]byte(input), &env); err != nil {
log.Fatal(err)
}
msg := kindHandlers[env.Type]()
if err := json.Unmarshal(raw, msg); err != nil {
log.Fatal(err)
}
if err := msg.Run(app); err != nil {
// ...
}
}
接下来是另外一种设想,加入定义的json字段都放在最外层,即没有了嵌套的msg字段
{
"type": "用来识别不同的json数据",
...
}
那需要umarshal两次json,第一次比对type字段,针对不同的type字段来unmarsh一次
package main
import (
"encoding/json"
"fmt"
"log"
)
const input = `
{
"type": "sound",
"description": "dynamite",
"authority": "the Bruce Dickinson"
}
`
type Envelope struct {
Type string
}
type Sound struct {
Description string
Authority string
}
func main() {
var env Envelope
buf := []byte(input)
if err := json.Unmarshal(buf, &env); err != nil {
log.Fatal(err)
}
switch env.Type {
case "sound":
var s struct {
Envelope
Sound
}
if err := json.Unmarshal(buf, &s); err != nil {
log.Fatal(err)
}
var desc string = s.Description
fmt.Println(desc)
default:
log.Fatalf("unknown message type: %q", env.Type)
}
}
本文是下述博客的翻译和整理,仅供参考
如何处理动态JSON in Go的更多相关文章
- C#动态实体集的反序列化(动态JSON反序列化)
一.使用场景 我们在将 JSON 反序列化实体集的时候,如果字段是固定的,那么我们序列化非常简单,对应字段写的实体集就可以了.比如下面这种: { "data":[ { " ...
- 『动态』动态JSON万能转换函数 + .Net40 dynamic动态数据绑定
不废话,调用代码: static void Main(string[] args) { string json = File.ReadAllText("2.txt", Encodi ...
- 动态Json字符串的解析
动态Json字符串的解析 对于传统的Json字符串,比如有规定属性的对象,通常都会采用反序列化的方式就可以了,例如下面的方式: DataContractJsonSerializer ser = new ...
- 【.NET深呼吸】如何反序列化动态JSON
.net本身除了支持SOAP.XML.二进制等序列化和反序列化,后来也加入了对JSON的序列化的支持.然而,在实际开发中,常常会遇到结构不确定的JSON对象,这些对象可能是其他代码动态生成的,你事先无 ...
- C# JToken类的使用,实现解析动态json数据、遍历、查找
在原来解析json数据是,一般都是用反序列化来实现json数据的解读,这需要首先知道json数据的结构并且建立相应的类才能反序列化,一旦遇到动态的json数据,这种方法就不使用. 为了解决动态解析js ...
- C#序列化和反序列化 之 dynamic 动态Json的反序列化
序列化和反序列化的常识不再赘述,如果不清楚这个,可以 参考一下其他人写的文章https://www.cnblogs.com/maitian-lf/p/3670570.html 总结的说, 序列化 是把 ...
- C# 反射(GetType) 获取动态Json对象属性值的方法
之前在开发一个程序,希望能够通过属性名称读取出属性值,但是由于那时候不熟悉反射,所以并没有找到合适的方法,做了不少的重复性工作啊! 然后今天我再上网找了找,被我找到了,跟大家分享一下. 其实原理并不复 ...
- JavaScript如何处理解析JSON数据详解
JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧. JSON 是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON ...
- Jackson动态JSON处理
https://www.baeldung.com/jackson-mapping-dynamic-object https://www.baeldung.com/jackson-deserializa ...
随机推荐
- [无效]网络流之Edmond-Karp算法
// 此博文为迁移而来,写于2015年2月2日,不代表本人现在的观点与看法.原始地址:http://blog.sina.com.cn/s/blog_6022c4720102vr12.html UP ...
- [HNOI2015]亚瑟王(概率期望,DP)
题目大意:很清晰了,不写了. $1\le T\le 444,1\le n\le 220,0\le r\le 132,0<p_i<1,0\le d_i\le 1000$. $p_i$ 和 $ ...
- c# EPPlus读取Excel里面的时间字段时,1900-01-01转成了1899-12-31
看到一篇文章:https://bbs.csdn.net/topics/70511379,5楼的回复: 我发现EXCEL有千年虫BUG,把1900年算成闰年了,2月有29天.1900年3月1日以后就没问 ...
- 【视频开发】【计算机视觉】相机标定(Camera calibration)原理、步骤
相机标定(Camera calibration)原理.步骤 author@jason_ql(lql0716) http://blog.csdn.net/lql0716 在图像测量过程以及机器视觉应用 ...
- GCN(Graph Convolutional Network)的简单公式推导
第一步:从前一个隐藏层到后一个隐藏层,对结点进行特征变换 第二步:对第一步进行具体实现 第三步:对邻接矩阵进行归一化(行之和为1) 邻接矩阵A的归一化,可以通过度矩阵D来实现(即通过D^-1*A来实现 ...
- mke2fs和mkfs命令使用
1.mke2fs命令 在Linux系统下,mke2fs命令可用于创建磁盘分区上的”ext2/ext3”文件系统. (1)语法 mke2fs(选项)(参数) (2)常用选项 -b<区块大小> ...
- Django中使用CORS实现跨域请求(转)
原文:https://blog.csdn.net/zizle_lin/article/details/81381322 跨域请求: 请求url包含协议.网址.端口,任何一种不同都是跨域请求. ...
- 『金字塔 区间dp』
金字塔 Description 虽然探索金字塔是极其老套的剧情,但是这一队 探险家还是到了某金字塔脚下.经过多年的研究,科 学家对这座金字塔的内部结构已经有所了解.首先, 金字塔由若干房间组成,房间之 ...
- Windows下非PE方式载荷投递方式研究
0. 引言 0x1:载荷是什么?在整个入侵过程中起到什么作用? 载荷的作用在整个入侵链路的作用起到纽带的作用,它借助于目标系统提供的某些功能:组件:执行环境,将攻击者的传递的恶意payload包裹起来 ...
- TCP和UDP的差异
TCP建立连接 如果有人问TCP如何建立连接?大部分的回答都是三次握手成功,就建立连接了. 那么握手的目的是什么呢? 1)告知对方自己的序号 2)初始化资源 例如以下握手案例,握手两次由客户端主动发起 ...