go面向对象方式操作JSON库实现四则运算

目录
  • 前言
  • 面向对象的方式操作 JSON
  • 实现原理
  • 对 JSON 做四则运算
  • 总结

前言

在之前实现的 JSON 解析器中当时只实现了将一个 JSON 字符串转换为一个 JSONObject,并没有将其映射为一个具体的 struct;如果想要获取值就需要先做断言将其转换为 map 或者是切片再来获,会比较麻烦。

decode, err := xjson.Decode(`{"glossary":{"title":"example glossary","age":1}}`)
assert.Nil(t, err)
glossary := v["glossary"].(map[string]interface{})
assert.Equal(t, glossary["title"], "example glossary")
assert.Equal(t, glossary["age"], 1)

但其实转念一想,部分场景我们甚至我们只需要拿到 JSON 中的某个字段的值,这样还需要先声明一个 struct 会略显麻烦。

于是我也打算增加类似的功能,使用方式如下:

最后还加上了一个四则运算的功能。

面向对象的方式操作 JSON

因为功能类似,所以我参考了 tidwall 的 API 但去掉一些我觉得暂时用不上的特性,并调整了一点语法。

当前这个版本只能通过确定的 key 加上 . 点符号访问数据,如果是数组则用 [index] 的方式访问下标。
[] 符号访问数组我觉得要更符合直觉一些。

以下是一个包含多重嵌套 JSON 的访问示例:

str := `
{
"name": "bob",
"age": 20,
"skill": {
    "lang": [
        {
            "go": {
                "feature": [
                    "goroutine",
                    "channel",
                    "simple",
                    true
                ]
            }
        }
    ]
}
}`
name := xjson.Get(str, "name")
assert.Equal(t, name.String(), "bob")
age := xjson.Get(str, "age")
assert.Equal(t, age.Int(), 20)
assert.Equal(t, xjson.Get(str,"skill.lang[0].go.feature[0]").String(), "goroutine")
assert.Equal(t, xjson.Get(str,"skill.lang[0].go.feature[1]").String(), "channel")
assert.Equal(t, xjson.Get(str,"skill.lang[0].go.feature[2]").String(), "simple")
assert.Equal(t, xjson.Get(str,"skill.lang[0].go.feature[3]").Bool(), true)

这样的语法使用个人觉得还是满符合直觉的,相信对使用者来说也比较简单。

返回值参考了 tidwall 使用了一个 Result 对象,它提供了多种方法可以方便的获取各种类型的数据

func (r Result) String() string
func (r Result) Bool() bool
func (r Result) Int() int
func (r Result) Float() float64
func (r Result) Map() map[string]interface{}
func (r Result) Array() *[]interface{}
func (r Result) Exists() bool

比如使用 Map()/Array() 这两个函数可以将 JSON 数据映射到 map 和切片中,当然前提是传入的语法返回的是一个合法 JSONObject 或数组。

实现原理

在实现之前需要先定义一个基本语法,主要支持以下四种用法:

  • 单个 key 的查询:Get(json,"name")
  • 嵌套查询: Get(json,"obj1.obj2.obj3.name")
  • 数组查询:Get(json,"obj.array[0]")
  • 数组嵌套查询:Get(json,"obj.array[0].obj2.obj3[1].name")

语法很简单,符合我们日常接触到语法规则,这样便可以访问到 JSON 数据中的任何一个值。

其实实现过程也不复杂,我们已经在上一文中实现将 JSON 字符串转换为一个 JSONObject 了。

这次只是额外再解析刚才定义的语法为 token,然后解析该 token 的同时再从生成好的 JSONObject 中获取数据。

最后在解析完 token 时拿到的 JSONObject 数据返回即可。

我们以这段查询代码为例:

首先第一步是对查询语法做词法分析,最终得到下图的 token

在词法分析过程中也可以做简单的语法校验;比如如果包含数组查询,并不是以 ] 符号结尾时就抛出语法错误。

接着我们遍历语法的 token。如下图所示:

每当遍历到 token 类型为 Key 时便从当前的 JSONObject 对象中获取数据,并用获取到的值替覆盖为当前的 JSONObject。

其中每当遇到 . [ ] 这样的 token 时便消耗掉,直到我们将 token 遍历完毕,这时将当前 JSONObject 返回即可。

在遍历过程中当遇到非法格式时,比如 obj_list[1.] 便会返回一个空的 JSONObject

语法校验这点其实也很容易办到,因为根据我们的语法规则,Array 中的 index 后一定紧接的是一个 EndArray,只要不是一个 EndArray 便能知道语法不合法了。

有兴趣的可以看下解析过程的源码:

https://github.com/crossoverJie/xjson/blob/cfbca51cc9bc0c77e6cb9c9ad3f964b2054b3826/json.go#L46

对 JSON 做四则运算

str := `{"name":"bob", "age":10,"magic":10.1, "score":{"math":[1,2]}}`
result := GetWithArithmetic(str, "(age+age)*age+magic")
assert.Equal(t, result.Float(), 210.1)
result = GetWithArithmetic(str, "(age+age)*age")
assert.Equal(t, result.Int(), 200)
result = GetWithArithmetic(str, "(age+age) * age + score.math[0]")
assert.Equal(t, result.Int(), 201)
result = GetWithArithmetic(str, "(age+age) * age - score.math[0]")
assert.Equal(t, result.Int(), 199)
result = GetWithArithmetic(str, "score.math[1] / score.math[0]")
assert.Equal(t, result.Int(), 2)

最后我还扩展了一下语法,可以支持对 JSON 数据中的整形(int、float)做四则运算,虽然这是一个小众需求,但做完我觉得还挺有意思的,目前在市面上我还没发现有类似功能的库,可能和小众需求有关

(0)

相关推荐

  • Golang栈结构和后缀表达式实现计算器示例

    目录 引言 问题 中缀.后缀表达式的计算 人利用中缀表达式计算值 计算机利用后缀表达式计算值 计算后缀表达式的代码实现 中缀表达式转后缀表达式 转换过程 转换的代码实现 总结 引言 只进行基本的四则运算,利用栈结构和后缀表达式来计算数学表达式的值. 本文代码:GitHub 运行效果: 问题 如果只能进行两个值的加减乘除,如何编程计算一个数学表达式的值? 比如计算 1+2*3+(4*5+6)*7,我们知道优先级顺序 () 大于 * / 大于 + - ,直接计算得 1+6+26*7 = 189 中缀

  • go json编译原理XJSON实现四则运算

    目录 前言 转义字符 性能优化 实现四则运算 总结 前言 在上一篇中介绍了xjson的功能特性以及使用查询语法快速方便的获取JSON中的值. 同时这次也更新了一个版本,主要是两个升级: 对转义字符的支持. 性能优化,大约提升了30%️. 转义字符 先说第一个转义字符,不管是原始JSON字符串中存在转义字符,还是查询语法中存在转义字符都已经支持,具体用法如下: str = `{"1a.b.[]":"b"}` get = Get(str, "1a\\.b\\.

  • golang 四则运算计算器yacc归约手写实现

    目录 缘起 目标 难点 总体流程 main.go tokens/tokens.go states/states.go lexer/lexer.go parser/tStackNode.go parser/parser.go 输出 缘起 最近拜读前桥和弥[日]的<<自制编程语言>> 开头一章便是教读者使用lex/yacc工具 制作四则运算器 其中yacc的移进/归约/梯度下降的思想很有启发 于是使用golang练习之 目标 制作一个四则运算器, 从os.Stdin读入一行表达式, 然

  • go面向对象方式操作JSON库实现四则运算

    目录 前言 面向对象的方式操作 JSON 实现原理 对 JSON 做四则运算 总结 前言 在之前实现的 JSON 解析器中当时只实现了将一个 JSON 字符串转换为一个 JSONObject,并没有将其映射为一个具体的 struct:如果想要获取值就需要先做断言将其转换为 map 或者是切片再来获,会比较麻烦. decode, err := xjson.Decode(`{"glossary":{"title":"example glossary"

  • C++使用JsonCpp库操作json格式数据示例

    本文实例讲述了C++使用JsonCpp库操作json格式数据的方法.分享给大家供大家参考,具体如下: 前言 JSON是一个轻量级的数据定义格式,比起XML易学易用,而扩展功能不比XML差多少,用之进行数据交换是一个很好的选择 JSON的全称为:JavaScript Object Notation ,顾名思义,JSON是用于标记javascript对象的,详情参考http://www.json.org/. 本文选择第三方库JsonCpp来解析json,JsonCpp是比较出名的c++解析库,在js

  • java 实现通过 post 方式提交json参数操作

    由于所爬取的网站需要验证码,通过网页的开发人员工具[F12]及在线http post,get接口测试请求工具(http://coolaf.com/)发现访问时加上请求头header 信息时可以跳过验证码校验. 而且该网站只接受post请求,对提交的参数也只接受json格式,否则请求失败. 现将通过 post 方式提交json参数的方法记录如下: import java.io.UnsupportedEncodingException; import java.net.URI; import jav

  • PHP7扩展开发之基于函数方式使用lib库的方法详解

    本文实例讲述了PHP7扩展开发之基于函数方式使用lib库的方法.分享给大家供大家参考,具体如下: 前言 首先说下什么是lib库.lib库就是一个提供特定功能的一个文件.可以把它看成是PHP的一个文件,这个文件提供一些函数方法.只是这个lib库是用c或者c++写的. 使用lib库的场景.一些软件已经提供了lib库,我们就没必要再重复实现一次.如,原先的mysql扩展,就是使用mysql官方的lib库进行的封装. 在本文,我们将建立一个简单的lib库,并在扩展中进行封装调用. 代码 基础代码 这个扩

  • C++操作json文件以及jsoncpp配置详解

    前言 json文件是比较轻量级的文件,格式简单,使用方便.用来存放信息相比其他方式有自己得天独厚的优势. 今天给大家分享的是如何利用C++来操作json文件. 如果你知道如何使用jsoncpp类库,可以不用看附,如果第一次使用,请先到最后,将环境配置好,再进行操作. 一.json文件简介 1.json文件 JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集,采用完

  • C++使用cjson操作Json格式文件(创建、插入、解析、修改、删除)

    目录 为什么要学习解析Json文件? 一.准备cJSON开源库 二.cJSON介绍 三.封装Json 四.解析Json 五.修改Json 六.删除Json 七.全部代码 八.总结 为什么要学习解析Json文件? 工作需要呗! 最近在工作项目中,有需求是需要进行解析Json字符串的,但是我只会使用QT去解析Json,且主管规定要使用C/C++语言去解析,说是为了方便移植到其他项目中进行使用… 没办法,只能硬着头皮,在网上找找有没有什么解析Json的开源库是C/C++可以使用的.找了许多,网上也提供

  • JS中操作JSON总结

    SON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 本文主要是对JS操作JSON的要领做下总结. 在JSON中,有两种结构:对象和数组. 1. 一个对象以"{"(左括号)开始,"}"(右括号)结束.每个"名称"后跟一

  • python标准库模块之json库的基础用法

    目录 前言 作用 loads,load的用法 dumps,dump的用法 结语 前言 json,全称为JavaScript Object Notation,也就是JavaScript对象标记,通过对象和数组的组合表示数据,虽然构造简洁但是结构化程度非常高,是一种轻量级的数据交换格式. 作用 主要用于将python对象编码为json格式输出或存储,以及将json格式对象解码为python对象. 一个 JSON 对象可以写为如下形式: [{ "name": "小明",

  • C++操作.json文件的超详细新手教程

    目录 1.JSON简介 1.1 什么是JSON: 1.2 JSON的优缺点: 1.3 JSON的存储: 2.jsoncpp库介绍 2.1 jsoncpp库的配置使用: 2.2 jsoncpp库内部构成: 3.json文件读取(例) 3.1 json文件: 3.2 源码: 3.3 结果图: 总结 1.JSON简介 1.1 什么是JSON: JSON是一种纯字符串形式的数据,它本身不提供任何方法(函数),非常适合在网络中进行传输.JavaScript.PHP.Java.Python.C++等编程语言

  • Python使用面向对象方式创建线程实现12306售票系统

    目前python 提供了几种多线程实现方式 thread,threading,multithreading ,其中thread模块比较底层,而threading模块是对thread做了一些包装,可以更加方便的被使用. 面向对象技术简介 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集合中每个对象所共有的属性和方法.对象是类的实例. 类变量:类变量在整个实例化的对象中是公用的.类变量定义在类中且在函数体之外.类变量通常不作为实例变量使用. 数据成员:类变量或者实例变量用于

随机推荐