假如要设计一个统计的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)
}
}

第一部分结束了,接下来还有来个地方可以提升

  1. 将定义的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. 结合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)
}
}

本文是下述博客的翻译和整理,仅供参考

  1. Dynamic JSON in Go
  2. Go JSON unmarshaling based on an enumerated field value

如何处理动态JSON in Go的更多相关文章

  1. C#动态实体集的反序列化(动态JSON反序列化)

    一.使用场景 我们在将 JSON 反序列化实体集的时候,如果字段是固定的,那么我们序列化非常简单,对应字段写的实体集就可以了.比如下面这种: { "data":[ { " ...

  2. 『动态』动态JSON万能转换函数 + .Net40 dynamic动态数据绑定

    不废话,调用代码: static void Main(string[] args) { string json = File.ReadAllText("2.txt", Encodi ...

  3. 动态Json字符串的解析

    动态Json字符串的解析 对于传统的Json字符串,比如有规定属性的对象,通常都会采用反序列化的方式就可以了,例如下面的方式: DataContractJsonSerializer ser = new ...

  4. 【.NET深呼吸】如何反序列化动态JSON

    .net本身除了支持SOAP.XML.二进制等序列化和反序列化,后来也加入了对JSON的序列化的支持.然而,在实际开发中,常常会遇到结构不确定的JSON对象,这些对象可能是其他代码动态生成的,你事先无 ...

  5. C# JToken类的使用,实现解析动态json数据、遍历、查找

    在原来解析json数据是,一般都是用反序列化来实现json数据的解读,这需要首先知道json数据的结构并且建立相应的类才能反序列化,一旦遇到动态的json数据,这种方法就不使用. 为了解决动态解析js ...

  6. C#序列化和反序列化 之 dynamic 动态Json的反序列化

    序列化和反序列化的常识不再赘述,如果不清楚这个,可以 参考一下其他人写的文章https://www.cnblogs.com/maitian-lf/p/3670570.html 总结的说, 序列化 是把 ...

  7. C# 反射(GetType) 获取动态Json对象属性值的方法

    之前在开发一个程序,希望能够通过属性名称读取出属性值,但是由于那时候不熟悉反射,所以并没有找到合适的方法,做了不少的重复性工作啊! 然后今天我再上网找了找,被我找到了,跟大家分享一下. 其实原理并不复 ...

  8. JavaScript如何处理解析JSON数据详解

    JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧. JSON 是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON ...

  9. Jackson动态JSON处理

    https://www.baeldung.com/jackson-mapping-dynamic-object https://www.baeldung.com/jackson-deserializa ...

随机推荐

  1. shell equal

    #!/bin/shzero=0 status=1 let status=0 if [[ $status -eq $zero ]];then echo "bu >= 3.6"e ...

  2. CURL SSL为6的由来

    起初,我直接用方倍工作室之前做微信接口调用时的http_request函数,发现能正常调用微信api的函数获取不了火星天气数据,然后又尝试用file_get_contents,仍然不行 一直出的错就是 ...

  3. ASP.NET Core WebApi构建API接口服务实战演练

    一.ASP.NET Core WebApi课程介绍 人生苦短,我用.NET Core!提到Api接口,一般会想到以前用到的WebService和WCF服务,这三个技术都是用来创建服务接口,只不过Web ...

  4. javascript 函数的暂停和恢复

    javascript 异步编程从来都是一个难题,最开始我们用 callback,但随之触发了回调地狱,于是"发明" Promise 解决 callback 嵌套过深的问题.然而由于 ...

  5. thinkPHP5如何使用rabbitmq

    thinkPHP5如何使用rabbitmq? 安装好 tp5 的 rabbitmq 扩展后,在项目根目录文件添加文件 rabbitmq.php 引导启动 rabbitmq. <?php defi ...

  6. 028 ElasticSearch----全文检索技术03---基础知识详解01-IK分词器和映射

    1.IK分词器 (1)安装 使用IK分词器可以实现对中文分词的效果.下载IK分词器:(Github地址:https://github.com/medcl/elasticsearch-analysis- ...

  7. Hystrix实现ThreadLocal上下文的传递 转

    springcloud微服务中, 服务间传输全局类参数,如session信息等. 一.问题背景 Hystrix有2个隔离策略:THREAD以及SEMAPHORE,当隔离策略为 THREAD 时,是没办 ...

  8. python 知识回顾

    第一章:搭建编程环境1.在Windows系统中搭建Python编程环境. 1.1 安装Python 访问http://python.org/downloads/ ,点击下载到本地,后安装. 1.2安装 ...

  9. 记:使用IScroll.js 开发picker日历组件遇到的问题及经验总结

    IScroll中文文档 第一个问题: 边界留白 就是这种,上边界(最小),下边界(最大)有两个列表的位置是不能选择的.解决的办法是: 在HTML中,添加空白节点就行了. 第二个问题:初始化之后的滚动停 ...

  10. Git系列 —— 记一次Mac上git push时总是403的错误

    问题: 今天从github上clone下一个项目,然后修改后git push时总是出现: remote:Permission to lixyou/rw-split-plugin.git defined ...