使用 Golang 实现一个 JSON 命令行工具

栏目: IT技术 · 发布时间: 1个月前

来源: studygolang.com

内容简介:首先先提一个问题,之前一直有在使用一个 json 的命令行工具下面就开始一步一步行动吧(如果想直接看代码可以直接拉到底部),我将这个项目命名为

本文转载自:https://studygolang.com/articles/29749,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有。

首先先提一个问题, "abc"123 或者 [1, 2, 3] 是不是一个合法的 json ?

之前一直有在使用一个 json 的命令行工具 jq ,这个 工具 是基于 flex 和 bison 来实现的(去了解这些是基于当年学习 php 的经历)。后来有段时间我又发现一个不错的词法和语法分析工具 antlr ,它支持多种语言的生成,并且本身也提供了多种语言的基本语法文件。所以我就想能不用基于它实现一个 go 语言版的 json 命令行工具。

下面就开始一步一步行动吧(如果想直接看代码可以直接拉到底部),我将这个项目命名为 jtlr

提供的功能

根据我自己常使用的场景,我要实现以下几个功能:

基本用法:

jtlr '{"a": 1}'

交互模式,可以多次输入,并且最好能支持上下切换:

jtlr -a

从标准输入中读取内容,可以格式化实时输出的日志:

tail -f xxx.log | jtlr -s

从文件中读取:

jtlr -f xxx.log

什么是 json

在动手之前,先要对 json 有一个全面的认识。先来大致看一下官网提供的 json 的 BNF 范式的起始部分:

json
    element

value
    object
    array
    string
    number
    "true"
    "false"
    "null"

...

element
    ws value ws

ws 是 whitespace 的缩写,即空白字符,忽略这个之后,即可简单清晰的看到 json 的内的有效数值。虽然我们常用的 json 内容都是 object 起的,但并不是一定要从 object 开始,所以对于文章开头那个问题,你有答案了吗?

在实现时我并没有去复制官网提供的 BNF,而是采用了 antlr4 提供的语法,关于它的实现,这里有一篇文章说明: https://andreabergia.com/a-grammar-for-json-with-antlr-v4/

简单来说,json 有七种的数据,其中 arrayobject 是可以再包含 value ,剩下五种就是基本的数值数据。

此外,还有一类比较特殊的情况,就是对 string 的用法:

member
    ws string ws ':' element

string 既可以是一个基本类型的 value ,也可以一个对象成员的键值。这会导致我们在对 string 做上色等处理时需要考虑着两种情况。

antlr4 提供的接口

使用下面的命令即可生成基于 go 语言的 lexer 和 parser:

antlr -Dlanguage=Go -o parser/ JSON.g4

接下来就是功能实现的工作了。

antlr4 生成的接口比较完备,包含每个分支逻辑进入、退出和错误节点访问的接口。并且有较好的错误纠正和提示机制。

但对于 json 本身这个 case,需要注意的是对 valuestring 。上面也有提到,所有七类数值都是 value ,所以都会触发 EnterValueExitValue 事件, string 同理。

对于 objectarray 来说,比较棘手的在于嵌套的数据,例如:

{"a": [134, {"a": 1}, true, [1, 2, 3], false]}

在使用 antlr4 提供的接口时,需要标注进入和退出的顺序。

交互模式下的问题

最开始我做了个非常简单的交互模式的实现:

reader := bufio.NewReader(os.Stdin)
for {
    fmt.Print(">>> ")
    text, err := reader.ReadString('\n')
    if err == io.EOF {
        break
    }
    if text == "\n" || text == "\r\n" {
        continue
    }
    fn(text)
}

但是在这种实现逻辑下,上下左右等按键会直接打印在屏幕上而无法正确处理,因为终端处于 cooked mode 下。go 本身也没有提供 tty 的封装。所以要进入 raw mode ,一种是通过直接 call 起命令行的方式:

func raw(start bool) error {
    r := "raw"
    if !start {
        r = "-raw"
    }

    rawMode := exec.Command("stty", r)
    rawMode.Stdin = os.Stdin
    err := rawMode.Run()
    if err != nil {
        return err
    }

    return rawMode.Wait()
}

另外一种是操作 stdin 的文件句柄,这样实现起来就相当复杂了。

出于兼容性和可维护性的考虑,我使用了 golang/crypto 提供的 terminal 的封装,这也是项目中除了 antlr 以外唯一一个引入的第三方包(如果算是第三方的话)。

但是这个包有一个问题是必须使用 \r\n 进行回车(官方的 issue 解释是一些历史原因吧啦吧啦),不然光标不会回到行首,但是 go 标准的 fmt 包中使用的 \n 换行,而 antlr 使用了 fmt 进行错误输出,所以需要对错误输出进行重载。

未完成

从开始构思到实现到当前阶段,大概耗时两个周末了。

由于前期偷懒,格式化输出全部使用的是 fmt,这里后续需要优化一下。

现在的实现对于 antlr 来说有点像用牛刀杀鸡,jq 本身支持的节点选取,这是后续实现的一个方向。

另外,go 官方虽然提供了官方的 json 序列化和反序列化工具,但是市面上也有一些第三方的实现被使用,我也想探讨一下实现方式。

另外,windows 下还没做完全的兼容测试。

最后,贴上项目地址: https://github.com/XiaoLer/jtlr-go

欢迎关注我们的微信公众号,每天学习Go知识

使用 Golang 实现一个 JSON 命令行工具

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

关注码农网公众号

关注我们,获取更多IT资讯^_^


为你推荐:

相关软件推荐:

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Linux命令行大全

Linux命令行大全

绍茨 (William E.Shotts) / 郭光伟、郝记生 / 人民邮电出版社 / 2013-3-1 / 69.00元

《Linux命令行大全》主要介绍Linux命令行的使用,循序渐进,深入浅出,引导读者全面掌握命令行的使用方法。 《Linux命令行大全》分为四部分。第一部分开始了对命令行基本语言的学习之旅,包括命令结构、文件系统的导引、命令行的编辑以及关于命令的帮助系统和使用手册。第二部分主要讲述配置文件的编辑,用于计算机操作的命令行控制。第三部分讲述了从命令行开始执行的常规任务。类UNIX操作系统,比如L......一起来看看 《Linux命令行大全》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具