如何在Go中从stdin解析无限的JSON数组?

huangapple go评论74阅读模式
英文:

How to parse an infinite json array from stdin in go?

问题

我正在尝试编写一个小型替代品,用于与符合此协议的i3bar进行通信的小程序,该协议是与i3status交换消息的。它们通过stdin和stdout进行消息交换。

在两个方向上的流都是无限的JSON对象数组。从i3bar到i3status(我想要替换的部分)的流的开头如下所示:

[
{"name": "some_name_1","instance": "some_inst_1","button": 1,"x": 213,"y": 35}
,{"name": "some_name_1","instance": "some_inst_2","button": 2,"x": 687,"y": 354}
,{"name": "some_name_2","instance": "some_inst","button": 1,"x": 786,"y": 637}
,{"name": "some_name_3","instance": "some_inst","button": 3,"x": 768,"y": 67}
...

这是一个表示点击的对象数组。该数组永远不会关闭。

我的问题是:解析这个的正确方法是什么?

显然,我不能使用json库,因为这不是一个有效的JSON对象。

英文:

I'm trying to write a small replacement for i3status, a small programm that comunicates with i3bar conforming this protocol. They exchange messeages via stdin and stdout.

The stream in both directions is an infinite array of json objects. The start of the stream from i3bar to i3status (which i want to replace) looks like this:

[
{"name": "some_name_1","instance": "some_inst_1","button": 1,"x": 213,"y": 35}
,{"name": "some_name_1","instance": "some_inst_2","button": 2,"x": 687,"y": 354}
,{"name": "some_name_2","instance": "some_inst","button": 1,"x": 786,"y": 637}
,{"name": "some_name_3","instance": "some_inst","button": 3,"x": 768,"y": 67}
...

This is an "array" of objects which represent clicks. The array will never close.

My question is now: What is the right way of parsing this?

Obviously I cannot use the json library because this is not a vaild json object.

答案1

得分: 6

编写一个自定义的读取函数(或解码器),实现“流式数组解析”的功能,具体步骤如下:

  1. 读取并丢弃前导空格。
  2. 如果下一个字符不是[,则返回错误(不能是一个数组)。
  3. 循环执行以下步骤:
    1. 调用json.Decoder.Decode解码为“下一个”项。
    2. 输出或处理“下一个”项。
    3. 读取并丢弃空格。
    4. 如果下一个字符是:
      1. 逗号,,则继续执行步骤#3的循环。
      2. 闭合括号],则退出步骤#3的循环。
      3. 否则返回错误(无效的数组语法)。
英文:

Write a custom reader function (or Decoder) which does a "streaming array parse" like so:

  1. Read and discard leading whitespace.
  2. If the next character is not a [ then return an error (can't be an array).
  3. While true do:
    1. Call json.Decoder.Decode into the "next" item.
    2. Yield or process the "next" item.
    3. Read and discard whitespace.
    4. If the next character is:
      1. A comma , then continue the for-loop in #3.
      2. A close bracket ] then exit the for-loop in #3.
      3. Otherwise return an error (invalid array syntax).

答案2

得分: 4

我正在为i3中的点击事件编写自己的处理程序。这就是我偶然发现这个线程的方式。

Golang标准库实际上确实可以做到所需的功能(Golang 1.12)。不确定你提问时是否已经这样做了?

// 从标准输入读取JSON解析器
decoder := json.NewDecoder(os.Stdin)

// 获取第一个标记 - 应该是一个'['
tk, err := decoder.Token()
if err != nil {
    fmt.Fprintln(os.Stderr, "无法从标准输入读取")
    return
}

// 根据文档,json.Delim可以是'['、']'、'{'或'}'
// 确保标记是一个分隔符
delim, ok := tk.(json.Delim)
if !ok {
    fmt.Fprintln(os.Stderr, "第一个标记不是分隔符")
    return
}

// 确保分隔符的值是'['
if delim != json.Delim('[') {
    fmt.Fprintln(os.Stderr, "第一个标记不是'['")
    return
}

// 解析器获取了上面的'['
// 因此,decoder.More()将返回true,直到看到一个闭合的']'为止 - 也就是永远不会返回false
for decoder.More() {
    // 使用相关的JSON结构标签创建一个Click结构体
    click := &Click{}

    // 这将阻塞,直到我们在标准输入上有一个完整的JSON对象
    err = decoder.Decode(click)
    if err != nil {
        fmt.Fprintln(os.Stderr, "无法解码点击事件")
        return
    }

    // 处理点击事件
}
英文:

I'm writing my own handler for click events in i3 as well. That's how I stumbled upon this thread.

The Golang standard library does actually do exactly what is required (Golang 1.12). Not sure it did when you asked the question or not?

// json parser read from stdin
decoder := json.NewDecoder(os.Stdin)

// Get he first token - it should be a '['
tk, err := decoder.Token()
if err != nil {
    fmt.Fprintln(os.Stderr, "couldn't read from stdin")
    return
}
// according to the docs a json.Delim is one [, ], { or }
// Make sure the token is a delim
delim, ok := tk.(json.Delim)
if !ok {
    fmt.Fprintln(os.Stderr, "first token not a delim")
    return
}
// Make sure the value of the delim is '['
if delim != json.Delim('[') {
    fmt.Fprintln(os.Stderr, "first token not a [")
    return
}
    
// The parser took the [ above
// therefore decoder.More() will return
// true until a closing ] is seen - i.e never 
for decoder.More() {
    // make a Click struct with the relevant json structure tags
    click := &Click{}

    // This will block until we have a complete JSON object
    // on stdin
    err = decoder.Decode(click)
    if err != nil {
            fmt.Fprintln(os.Stderr, "couldn't decode click")
            return
    }
    // Do something with click event
}

答案3

得分: 0

你要找的是一个用于 JSON 的流式 API。有很多可用的选项,一个快速的谷歌搜索揭示了这个项目,它列出了流式传输作为其高级功能之一。

英文:

What you are looking for is a Streaming API for JSON. There are many available a quick Google search revealed this project that does list Streaming as one of it's advance features.

huangapple
  • 本文由 发表于 2015年4月3日 04:01:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/29421470.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定