Golang JSON的进阶用法实例讲解

痛点

json 是当前最常用的数据传输格式之一,纯文本,容易使用,方便阅读,在通信过程中大量被使用。

你是否遇到过json中某个字段填入某种类型都适合而陷入两难境地? (例如:定义了一个port字段,你却不知道是填入 8080 ,还是 “8080” 的尴尬局面)

你是否遇到过json反解析报错是因为填入字段的类型不匹配导致的?例如:

json: cannot unmarshal number into Go struct field Host.port of type string

你是否有json某字段兼容2种或者多种的数据结构的需求?

你是否想让程序更优雅,更具有适配性,而不在被这些小细节头痛?

如果你有或者你想,获取你可以看看这篇文章。

重现问题

我们给了用户一个json如下:

{
 "name":"yulibaozi",
 "port":8080
}

但是,业务方却误填了”8080”,结果我们程序反解析报错,导致业务失败。

json: cannot unmarshal number into Go struct field Host.port of type string

或许你认为这是业务方的问题,但我认为我们可以更优雅的解决这个问题。

如何解决问题

我们先定义了一个结构体

type Host struct {
 Name string `json:"name"`
 Port Port `json:"port"`
}

心细的你会发现,Port既不是int也不是string类型,而是Port类型,而Port类型是:

type Type int

const (
 Int Type = iota
 String
)

type Port struct {
 Type Type
 IntVal int
 StrVal string
}

在Port结构体中,我们发现了Type类型, 而Type类型包括了int,string两种类型。接下来就非常重要了,我们需要实现以下这两个接口。

json.Unmarshaller interface
json.Marshaller interface

实现代码如下:

type Port struct {
 Type Type
 IntVal int
 StrVal string
}

// 实现 json.Unmarshaller 接口
func (port *Port) UnmarshalJSON(value []byte) error {
 if value[0] == '"' {
  port.Type = String
  return json.Unmarshal(value, &port.StrVal)
 }
 port.Type = Int
 return json.Unmarshal(value, &port.IntVal)
}

// 实现 json.Marshaller 接口
func (port Port) MarshalJSON() ([]byte, error) {
 switch port.Type {
 case Int:
  return json.Marshal(port.IntVal)
 case String:
  return json.Marshal(port.StrVal)
 default:
  return []byte{}, fmt.Errorf("impossible Port.Type")
 }
}

接下来测试:

测试反解析

测试反解析int

给出json数据:

{"name":"yulibaozi","port":8090}

反解析得到的结构体数据如下:

}

测试反解析string:

给出json数据:

{"name":"yulibaozi","port":"8090"}

反解析得到的结构体数据如下:

}

测试编码的json

测试编码int的结构体如下:

host := &Host{
   Name: "yulibaozi",
   Port: Port{
     Type:  Int,
     IntVal: 8080,
   },
 }

编码后的json如下:

{"name":"yulibaozi","port":8080}

测试编码string的结构体如下:

host := &Host{
   Name: "yulibaozi",
   Port: Port{
     Type:  String,
     StrVal: "8080",
   },
 }

编码后的json数据如下:

{"name":"yulibaozi","port":"8080"}

在反编码测试中,你会发现当json填入的类型不同时,会编码到结构体中对应的字段中。

在编码测试中, 具体编码那个数据是由Type来确定的。

总结

其实,这篇文章只是分享了下json中使用的小技巧,他打破了在使用json时,需要呆板的数据结构的印象,转而走向了多变,灵活跳脱的风格,其实,这这个小tips的核心在于实现Unmarshaller,Marshaller这两个结构体,他们的实现是这个分享的关键,当然,你可以实现如开篇所说的那样,json某字段兼容2种及以上结构,当然,你也可以对yaml,toml等进行折腾,都会得到你想要的答案。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Golang map如何生成有序的json数据详解

    前言 本文主要给大家介绍了关于Golang map生成有序json数据的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 先来看一段 Golang 生成 json 的代码,首先定义了一个 map[string]interface{}  的变量,然后存一些值,这里要注意的是 previews 字段,为了浏览器获取到的 json 数据是有序的,所以定义了一个 map[int]map[string]string 的类型,加上了一个表示顺序的键: list := make(map[strin

  • golang实现sql结果集以json格式输出的方法

    本文实例讲述了golang实现sql结果集以json格式输出的方法.分享给大家供大家参考,具体如下: 复制代码 代码如下: func getJSON(sqlString string) (string, error) {     stmt, err := db.Prepare(sqlString)     if err != nil {         return nil, err     }     defer stmt.Close()     rows, err := stmt.Query

  • Go语言的JSON处理详解

    Go语言内建对JSON的支持.使用Go语言内置的encoding/json标准库,开发者可以轻松使用Go程序生成和解析JSON格式的数据.在Go语言实现JSON的编码和解码时,遵循RFC4627协议标准. 1.编码为JSON格式 使用json.Marshal()函数可以对一组数据进行JSON格式的编码.json.Marshal()函数的声明如下: 假如有如下一个Book类型的结构体: 并且有如下一个Book类型的实例对象: 然后,我们可以使用json.Marshal()函数将gobook实例生成

  • golang如何自定义json序列化应用详解

    前言 Go语言作为一个由Google开发,号称互联网的C语言的语言,自然也对JSON格式支持很好.下面这篇文章主要介绍了关于golang自定义json序列化应用的相关内容,下面话不多说了,来一起看看详细的介绍吧 问题引入 当某个struct存在某个字段为string或者[]byte类型但是实际上保存的内容是json格式的数据时,对其进行json序列化,比如 type Message struct { From string `json:"from"` To string `json:&

  • golang如何修改json文件内容的方法示例

    使用一个例子说明golang如何访问和修改json文件:主要分三步: 从文件读入json串 把json串转变成golang对象 遍历或者修改json值 写回文件 假定用户输入json串为: { "user": { "mspid": "admin", "email": "admin@domain.com" }, "nodes": [ { "name": "no

  • golang中json反序列化可能遇到的问题

    前言 在golang中,当浮点数超过一定数值的时候,golang会把它弄成科学计数法的形式进行显示(好像只要大于七位数就变成科学计数法了) var val float64 val = 1000000 fmt.Println(val) // ==> 1e+06 而在日常开发中,我们经常遇到这样一个问题,就是要反序列化前端传递来的json,因为数据结构未知,所以我们便会使用map[string]interface{}来接收反序列化的结果.由于golang将json解析到interface{}类型的时

  • Golang中使用JSON的一些小技巧分享

    前言 有的时候上游传过来的字段是string类型的,但是我们却想用变成数字来使用. 本来用一个json:",string" 就可以支持了,如果不知道golang的这些小技巧,就要大费周章了. 参考文章: JSON and struct composition in Go 临时忽略struct字段 type User struct { Email string `json:"email"` Password string `json:"password&qu

  • 利用Golang解析json数据的方法示例

    本文主要给大家介绍的是关于Golang解析json数据的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍: 使用 Golang 解析 json 数据,这种 json 格式是对象的数组,官方文档有一个示例: var jsonBlob = []byte(`[ {"Name": "Platypus", "Order": "Monotremata"}, {"Name": "Quoll

  • golang json性能分析详解

    前言 众所周知Json 作为一种重要的数据格式,具有良好的可读性以及自描述性,广泛地应用在各种数据传输场景中.Go 语言里面原生支持了这种数据格式的序列化以及反序列化,内部使用反射机制实现,性能有点差,在高度依赖 json 解析的应用里,往往会成为性能瓶颈,好在已有很多第三方库帮我们解决了这个问题,但是这么多库,对于像我这种有选择困难症的人来说,到底要怎么选择呢,下面就给大家来一一分析一下 ffjson go get -u github.com/pquerna/ffjson 原生的库性能比较差的

  • Golang JSON的进阶用法实例讲解

    痛点 json 是当前最常用的数据传输格式之一,纯文本,容易使用,方便阅读,在通信过程中大量被使用. 你是否遇到过json中某个字段填入某种类型都适合而陷入两难境地? (例如:定义了一个port字段,你却不知道是填入 8080 ,还是 "8080" 的尴尬局面) 你是否遇到过json反解析报错是因为填入字段的类型不匹配导致的?例如: json: cannot unmarshal number into Go struct field Host.port of type string 你

  • Vue2.0基于vue-cli+webpack Vuex的用法(实例讲解)

    在这之前,我已经分享过组件与组件的通信机制以及父子组件之间的通信机制,而我们的vuex就是为了解决组件通信问题的 vuex是什么东东呢? 组件通信的本质其实就是在组件之间传递数据或组件的状态(这里将数据和状态统称为状态),但可以看到如果我们通过最基本的方式来进行通信,一旦需要管理的状态多了,代码就会变得十分臃肿和庞大.对所有状态的管理便会显得力不从心,因此,vuex出现了,他就是帮助我们把公用的状态全抽出来放在vuex的容器中,然后根据一定的规则来进行管理,我们赶紧来用一下吧,想要掌握vuex的

  • 基于ES6 Array.of的用法(实例讲解)

    ES6为Array增加了of函数用已一中明确的含义将一个或多个值转换成数组. 因为,用new Array()构造数组的时候,是有二意性的. 构造时,传一个参数,表示生成多大的数组. 构造时,传多个参数,每个参数都是数组的一个元素. const arr1 = new Array() const arr2 = new Array(5) const arr3 = new Array(1, 3, '白色', {p1: 'v1'}) console.log('%s', JSON.stringify(arr

  • Golang单元测试与覆盖率的实例讲解

    1 概述 C/C++和Java(以及大多数的主流编程语言)都有自己成熟的单元测试框架,前者如Check,后者如JUnit,但这些编程框架本质上仍是第三方产品,为了执行单元测试,我们不得不从头开始搭建测试工程,并且需要依赖于第三方工具才能生成单元测试的覆盖率. 相比之下,Go语言官方则提供了语言级的单元测试支持,即testing包,而且仅通过go工具本身就可以方便地生成覆盖率数据,也就是说,单元测试是Go语言的自带属性,除了好好设计自己的单元测试用例外,开发者不需要操心工程搭建的任何细节.没错,G

  • Golang工作池的使用实例讲解

    目录 一.概念 二.实例 1.简单示例 2.读入数据 一.概念 我们可以将工作池理解为线程池.线程池的创建和销毁非常消耗资源,所以专门写一个pool,每次用过的线程池再放回pool中而不是销毁.不过在Go语言中不会使用系统的线程,而是使用goroutine.gorotine的创建和销毁比系统线程的消耗要小的多,而且goroutine没有标号.所以goroutine的pool就不再时线程池,而是work pool(工作池). 虽然goroutine的系统消耗较小,但也不能随意在编码时使用go fu

  • 基于多线程中join()的用法实例讲解

    Thread中,join()方法的作用是调用线程等待该线程完成后,才能继续用下运行. public class TestThread5 { public static void main(String[] args) throws InterruptedException { Runner0 run5 = new Runner0(); Thread th5 = new Thread(run5); th5.start(); th5.join();//join()方法用在此处是为了等待主线程结束后运

  • Java获取json数组对象的实例讲解

    如下所示: JSONArray jsonArray1 = jsonObject.getJSONArray("result"); for (int i = 0; i < jsonArray1.length(); i++) { JSONObject temp = (JSONObject) jsonArray1.get(i); String x = temp.getString("x"); String y = temp.getString("y"

  • vue进行图片的预加载watch用法实例讲解

    watch应用场景 我想信图片预加载大家肯定都有接触过,当图片量大的时候,为了保证页面图片都加载出来的时候,我们才把主页面给显示出来,再进行一些ajax请求,或者逻辑操作 那此时你用computed对这种监听一个数据然后进行一系列逻辑操作和ajax请求,那watch再适合不过了,如果用computed的话那你连实现都实现不了,只有用watch监听 <template> <div v-show=show> <img src="https://img.alicdn.co

  • python ChainMap管理用法实例讲解

    说明 1.ChainMap的主要用例是提供一种有效的方法来管理多个范围或上下文,并处理重复键的访问优先级. 2.当有多个存储重复键的字典访问它们的顺序时,这个功能非常有用. 在ChainMap文档中找到一个经典的例子,它模拟Python如何分析不同命名空间中的变量名称. 当Python搜索名称时,它会依次搜索当地.全局和内置的功能域,直到找到目标名称.Python作用域是将名称映射到对象的字典. 为了模拟Python的内部搜索链,可以使用链映射. 实例 >>> import builti

  • jQuery操作JSON的CRUD用法实例

    本文实例讲述了jQuery操作JSON的CRUD用法.分享给大家供大家参考.具体实现方法如下: 复制代码 代码如下: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">  <html xmlns="http://www.w3.org/1999/xht

随机推荐