Golang实现Json分级解析及数字解析实践详解

目录
  • 一、背景介绍
  • 二、解决方案
    • (1)将Json直接解析为map
    • (2)解析部分json struct的方法 (json.RawMessage的用法)
    • (3) json.Number类型的使用

一、背景介绍

在go语言开发过程中经常需要将json字符串解析为struct,通常我们都是根据json的具体层级关系定义对应的struct,然后通过json.Unmarshal()命令实现json到struct对象的转换,然后再根据具体逻辑处理相应的数据。

你是否遇到过在无法准确确定json层级关系的情况下对json进行解析的需求呢?

接下来我将介绍一次解析不确定的json对象的经历,以及遇到的问题和解决方法。

假设我们需要调用某个http接口从而获得一件商品的相似品推荐结果,该服务的输入参数只有一个skuId参数,接口的返回数据时json格式,并且同时存在两种返回参数。我们无法通过入参判断接口返回哪一种结果(估计这种异常是http服务开发者无意导致的,但是没办法,我们必须基于该服务接口开发某种功能。)

具体的,返回结果有两种情况

第一种

{
  "return": "0",
  "result":[
    {
      "goods_id": 37278077211,
      ....
      "shop_name": "xxxxx",
    },
    {
      "goods_id": 236492067349,
      ....
      "shop_name": "xxxxx",
    }
    ]
}

第二种

{
  "return": "0",
  "result": {
    "data": [
      {
        "goods_id": 58054798450,
        ......
        "shop_name": "xxxxxxxx"
      },
      {
        "goods_id": 27395404673,
        ......
        "shop_name": "xxxxxxxx"
      }
    ]
  }
}

二、解决方案

(1)将Json直接解析为map

由于在解析前我们并不能确定result到底是一个struct还是一个Slice,因此我们也无法直接利用json.Unmarshal一步解出对应的struct对象。好在我们知道所有json都可以直接解析成map[string]interface{}的结构,因此我们可以将json先转化为map,然后根据结构名key去决定后续的转换流程,具体代码如下:

var object interface{}
var data interface{}

err := json.Unmarshal([]byte(jsonStr),&object)
if err != nil{
    fmt.Printf("unmarshal %s error: %s\n",jsonStr,err.Error())
}

//判断returnCode
ret := object.(map[string]interface{})["return"]
if ret != 0{
    fmt.Println("the response of http error")
}

//判断result是何种类型
result := object.(map[string]interface{})["result"]
resultType := reflect.TypeOf(result)

if resultType.Kind() == reflect.Map{
    data = result.(map[string]interface{})["data"]
}

if resultType.Kind() == reflect.Slice{
    data = result
}

//解析goods_id
var skuList []int64
for _,v :=  range data.([]interface{}){
    preSku := v.(map[string]interface{})["goods_id"].(float64)
    skuList = append(skuList,int64(preSku))
}

fmt.Printf("the skuLst = %+v\n",skuList)

这种方式的优点是只需要Unmarshal一次,缺点是每一级都需要显示的去做类型转化,书写起来比较繁琐。尤其是json本身结构复杂,其中只有一小部分需要确定具体类型的情况下,解析过程会更加繁琐复杂。

那么是否可以只解析确定部分,不确定的部分先保留[]byte的原始格式,按map解析呢?

答案是肯定的。这时候就需要用到json.RawMessage字段类型了

(2)解析部分json struct的方法 (json.RawMessage的用法)

在解析json过程中,有时我们可能只需要解析json的某一部分数据,比如,当json中只有一部分是我们需要的数据,或者我们需要先解析一部分数据,才能根据解析的部分数据来决定剩余数据如何解析。我们继续以上面的需求为例。此时需要我们预先定义需要解析的部分

type RespStruct struct {
    RetCode int `json:"return"`
    Result json.RawMessage `json:"result"`
}

我们首先解析return字段。result字段内容将继续保持[]byte类型的状态。接下来我们继续解析剩余部分

var object RespStruct

err := json.Unmarshal([]byte(jsonStr2),&object)
if err != nil{
    fmt.Printf("unmarshal %s error: %s\n",jsonStr,err.Error())
}

//判断returnCode
if object.RetCode != 0{
    fmt.Println("the response of http error")
}

//判断result是何种类型
var data interface{}

err = json.Unmarshal(object.Result,&data)
if err != nil{
    fmt.Printf("unmarshal %s error: %s\n",object.Result,err.Error())
}

resultType := reflect.TypeOf(data)

if resultType.Kind() == reflect.Map{
    data = data.(map[string]interface{})["data"]
}

//解析goods_id
var skuList []int64
for _,v :=  range data.([]interface{}){
    preSku := v.(map[string]interface{})["goods_id"].(float64)
    skuList = append(skuList,int64(preSku))
}

fmt.Printf("the skuLst = %+v\n",skuList)

看到这里,有人可能会产生疑问,我可不可以将不需要解析的字段定义为[]byte 类型呢,毕竟解析为rawMessage类型后,该字段本身也是[]byte。通过实验我们发现是不可以的,如果将rawMessage替换为 []byte类型,解析过程会返回错误,因为在json包中,虽然RawMessage类型时 []byte的别名,但是解析过程中,只处理rawMessage类型(golang 是强类型编程语言)。

type RawMessage []byte

(3) json.Number类型的使用

在上个实验中,有些同学可能发现,为什么goods_id字段的类型先由interface{}类型转为float64,然后才被转换为我们需要的int64呢?

这是因为在 json 中是没有整型和浮点型之分的,当我们利用json 包中的 Unmarshal 方法将数字类型解析为interface{}时,它就会将把所有数字类型全部转换为和规范最接近的float64类型。如果我们希望更加方便的将数字类型准换为指定的类型,就需要用到json.Number这个类型。具体如下:

var object RespStruct

err := json.Unmarshal([]byte(jsonStr),&object)
if err != nil{
    fmt.Printf("unmarshal %s error: %s\n",jsonStr,err.Error())
}

//判断returnCode
if object.RetCode != 0{
    fmt.Println("the response of http error")
}

//判断result是何种类型
var data interface{}

decoder := json.NewDecoder(bytes.NewReader(object.Result))
decoder.UseNumber()
decoder.Decode(&data)

resultType := reflect.TypeOf(data)

if resultType.Kind() == reflect.Map{
    data = data.(map[string]interface{})["data"]
}

//解析goods_id
var skuList []int64
for _,v :=  range data.([]interface{}){
    preSku,err := v.(map[string]interface{})["goods_id"].(json.Number).Int64()
    if err != nil{
        fmt.Printf("get goods_id error")
    }
    skuList = append(skuList,preSku)
}

fmt.Printf("the skuLst = %+v\n",skuList)

利用json.Number类型可以让我们很方便的将数字类型转换成我们想要的类型,除了转换为int64类型外,还支持转换为float64和string。

为了进一步了解json.Number的实现,我们查看相关源码可以发现

//decode.go

// A Number represents a JSON number literal.
type Number string

// String returns the literal text of the number.
func (n Number) String() string { return string(n) }

// Float64 returns the number as a float64.
func (n Number) Float64() (float64, error) {
    return strconv.ParseFloat(string(n), 64)
}

// Int64 returns the number as an int64.
func (n Number) Int64() (int64, error) {
    return strconv.ParseInt(string(n), 10, 64)
}

通过源码我们可以发现json.Number本身是string类型,只是在json包中被定义了别名,然后通过封装的三个方法,实现了将string转换为int64和float64类型的方法。

以上就是Golang实现Json分级解析及数字解析实践详解的详细内容,更多关于Golang Json分级解析的资料请关注我们其它相关文章!

(0)

相关推荐

  • 解决golang json解析出现值为空的问题

    我是通过beego框架,将请求过来的json进行解析,并将值保存在结构体中 --------------------1--------------------- request := UpdateCommentRequestData{} req := common.Request{Data: request} err := json.Unmarshal(controller.Ctx.Input.RequestBody, &req) --------------------------------

  • Golang实现解析JSON的三种方法总结

    目录 背景 示例Json 例子 解释 1)反序列化成map 2)反序列化成对象 3)复杂json的解析 总结 背景 这是一篇写给0-1年新人的文章,短平快的教会你如何解析json字符串. 示例Json 假设有如下json字符串: { "userName":"admin", "nick_name":"管理员", "info":{ "age":18 }, "extra":

  • Golang 如何解析和生成json

    JSON(Javascript Object Notation)是一种轻量级的数据交换语言,以文字为基础,具有自我描述性且易于让人阅读.尽管JSON是JavaScript的一个子集,但JSON是独立于语言的文本格式,并且采用了类似于C语言家族的一些习惯.JSON与XML最大的不同在于XML是一个完整的标记语言,而JSON不是.JSON由于比XML更小.更快,更易解析,以及浏览器的內建快速解析支持,使得其更适用于网络数据传输领域. Golang自带的JSON解析库encoding/json,可以用

  • golang生成JSON以及解析JSON

    目录 一.JSON解析到结构体 1.JSON名称/值对 2.JSON值 3.JSON数据 4.JSON对象 5.JSON数组 6.JSON布尔值 7.JSON null 二.JSON转map 三.生成JSON 一.JSON解析到结构体 在介绍这部分之前先简要介绍一下Json语法 JSON 语法是 JavaScript 语法的子集.JSON 语法是 JavaScript 对象表示法语法的子集. 数据在名称/值对中 数据由逗号分隔 大括号保存对象 中括号保存数组 1.JSON名称/值对 "name&

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

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

  • Golang实现Json分级解析及数字解析实践详解

    目录 一.背景介绍 二.解决方案 (1)将Json直接解析为map (2)解析部分json struct的方法 (json.RawMessage的用法) (3) json.Number类型的使用 一.背景介绍 在go语言开发过程中经常需要将json字符串解析为struct,通常我们都是根据json的具体层级关系定义对应的struct,然后通过json.Unmarshal()命令实现json到struct对象的转换,然后再根据具体逻辑处理相应的数据. 你是否遇到过在无法准确确定json层级关系的情

  • Golang配置解析神器go viper使用详解

    目录 前言 viper简介 功能 viper配置优先级 安装viper 支持哪些文件格式 key大小写问题 使用指南 如何访问viper的功能 配置默认值 读取配置文件 写配置文件 WriteConfig SafeWriteConfig WriteConfigAs SafeWriteConfigAs 监听配置文件 从io.Reader读取配置 显示设置配置项 注册和使用别名 读取环境变量 与命令行参数搭配使用 pflag 扩展其他flag 远程key/value存储支持 访问配置 直接访问 序列

  • Go语言JSON解析器gjson使用方法详解

    目录 gjson 安装 使用 gjson GJSON 是一个Go包,它提供了一种从json文档中获取值的快速简单的方法.它具有单行检索.点符号路径.迭代和解析 json 行等功能. 还可以查看SJSON以修改 json,以及JJ命令行工具. 本自述文件是如何使用 GJSON 的快速概述,有关更多信息,请查看GJSON 语法. github 的地址在这里. 安装 安装gjson,使用的是go传统的安装方法: go install github.com/tidwall/gjson@latest 在文

  • TF-IDF算法解析与Python实现方法详解

    TF-IDF(term frequency–inverse document frequency)是一种用于信息检索(information retrieval)与文本挖掘(text mining)的常用加权技术.比较容易理解的一个应用场景是当我们手头有一些文章时,我们希望计算机能够自动地进行关键词提取.而TF-IDF就是可以帮我们完成这项任务的一种统计方法.它能够用于评估一个词语对于一个文集或一个语料库中的其中一份文档的重要程度. 在一份给定的文件里,词频 (term frequency, T

  • JSON创建键值对(key是中文或者数字)方式详解

    先准备好一个空的json对象 var obj = {}; 1. 最原始的方法 obj.name = 'zhangsan'; //这种方式很简单的添加了一个键值对 //输出:{name:"zhangsan"} //缺点:这边的name不能是对象 /* 比如: var name = 'tom'; obj.name = 'zhangsan'; 输出obj:{name:'zhangsan'} 中文可以使用,但是数字不能使用 obj.家="中国"; obj.88(不能这么使用

  • Python解析多帧dicom数据详解

    概述 pydicom是一个常用python DICOM parser.但是,没有提供解析多帧图的示例.本文结合相关函数和DICOM知识做一个简单说明. DICOM多帧数据存储 DICOM标准中关于多帧数据存储的最重要一部分说明是PS3.5 Annex A.4 A.4 Transfer Syntaxes For Encapsulation of Encoded Pixel Data. 无论何时,Pixel Data都存放在Pixel Data (7FE0,0010)中.有可能是直接存放的(nati

  • OpenJDK源码解析之System.out.println详解

    一.前戏 可能不少小伙伴习惯在代码中使用sout打印一些信息,就像这样: System.out.println("hello world!") 做为一位资深干码人,本着弘扬党求真务实的精神,必须得来看看这个sout有何玄机~~ 首先看调用就知道,out是System类的一个公共静态成员变量,进入System.java中: public final static PrintStream out = null; 嗯,不止是public,还是final的.不管,来找找out是在哪里赋值的..

  • Java MeteoInfo解析与绘图代码教程详解

    最近太忙了,终于有时间继续写了,上文说到了基本上的绘图方法,但缺少色阶呈现,一般图叠加着地图上,后端不需要管色阶,但也要注意web页面色阶和我们的生成色阶一定要对的上 对于后端导出图片的话,就需要添加色阶了,这一文很简单,就涉及色阶,名称,网格刻度线 顺便对代码我进行了优化,看起来更直接 MeteoDataInfo meteoDataInfo = new MeteoDataInfo(); meteoDataInfo.openMICAPSData("D:\\解析数据\\cldas\\cldas\\

  • Python实现解析yaml配置文件的示例详解

    目录 楔子 字典 数组 标量 引用 生成 yaml 文件 楔子 前面我们介绍了 ini 格式的配置文件,本次来看看 yaml,它的表达能力相比 ini 更加的强大.yaml 文件以 .yml 结尾,在介绍它的语法结构之前我们先来看看 yaml 的一些基本规则. 大小写敏感: 使用缩进表示层级关系,并且缩进只能用空格.不可以使用 tab 键.缩进的空格数目不重要,只要相同层级的元素左侧对齐即可: # 表示注释,# 到行尾的所有字符都会被忽略: yaml 支持的数据结构有以下三种: 字典:键值对的集

  • Python实现解析ini配置文件的示例详解

    目录 楔子 ini 文件 特殊格式 小结 楔子 在开发过程中,配置文件是少不了的,只不过我们有时会将 py 文件作为配置文件(config.py),然后在其它的模块中直接导入.这样做是一个好主意,不过配置文件是有专门的格式的,比如:ini, yaml, toml 等等. 而对于 Python 而言,也都有相应的库来解析相应格式的文件,下面我们来看看 ini 文件要如何解析. ini 文件 先来了解一下 ini 文件的格式: [satori] name = 古明地觉 age = 16 where 

随机推荐