【问题标题】:How to parse an infinite json array from stdin in go?如何从go中的stdin解析一个无限的json数组?
【发布时间】:2015-06-07 21:51:45
【问题描述】:

我正在尝试为 i3status 编写一个小替代品,这是一个与 i3bar 通信并符合 this 协议的小程序。他们通过标准输入和标准输出交换消息。

两个方向的流是一个无限的 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 对象。

【问题讨论】:

  • 肯定会有更好的办法,不过最简单的大概就是把开头的,去掉,然后用json库解析剩下的行:那就是json对象,对吧? (并且在留下此评论时可能会发布更好的方法,因此请改用它;)这就是为什么这不是答案:))
  • 没错。其余的只是一个 json 对象。我不能做这种骇人听闻的事情。我会恨自己;)
  • 严格来说,这不是有效的 JSON。一个有效的 JSON 数组必须有一个终止的右方括号 (]),这意味着它不能是无限的。我不希望 JSON 解析库能够通过正常方式处理它,尽管如果它公开它,您可能能够调用它的一些解析功能。

标签: json go jsonstream


【解决方案1】:

您正在寻找的是 JSON 的流式处理 API。有许多可用的快速 Google 搜索显示 this 项目确实将流列为其高级功能之一。

【讨论】:

  • 感谢您的快速回答。我现在自己在 Google 上搜索了 Go JSON Streaming API,但遗憾的是主要部分只是发送(作为您的链接)。接收完全是关于换行符分隔的列表({...}\n{...}\n{...}...)并且没有无限数组。为此,我将只使用 Scanners 和 json unmarshaling,这在我的案例中似乎不是惯用的方式。
  • 很遗憾听到这个消息。假设您无法控制这个无限输入,因为它将以数组的形式出现,而不是像您需要的那样拆分。您可以在忽略开始和结束框括号之前编写一个“预处理器”进行清理,这是相对微不足道的。现在忽略逗号,考虑创建一个值为 1 的计数器,每次 start { 递增一个计数器,每 } 递减一个计数器 - 当计数器为 1 时 - 忽略传入的逗号(并根据需要替换为 \n API)。
【解决方案2】:

编写一个自定义读取器函数(或解码器),它像这样执行“流式数组解析”:

  1. 读取并丢弃前导空格。
  2. 如果下一个字符不是[,则返回错误(不能是数组)。
  3. 当真时:
    1. 呼叫json.Decoder.Decode进入“下一个”项目。
    2. 让出或处理“下一个”项目。
    3. 读取并丢弃空格。
    4. 如果下一个字符是:
      1. 逗号 , 然后继续 #3 中的 for 循环。
      2. 右括号 ] 然后退出 #3 中的 for 循环。
      3. 否则返回错误(无效的数组语法)。

【讨论】:

  • 一个基本的状态机是要走的路。
  • 如何获得“下一个”项目?我应该只使用扫描仪并在换行符上使用拆分功能进行拆分吗?
  • @TristanStorch:只需调用 json.Decoder.Decode(...) - 从文档中,它“从其输入中读取下一个 JSON 编码值”并且应该正确处理任何不相关的空格。
  • @maerics 不错。非常感谢!
  • 备案:我用this 方法解决了它。 json.Decoder 有一个棘手的问题。
【解决方案3】:

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

Golang 标准库实际上完全符合要求(Golang 1.12)。不确定你问这个问题的时候是不是这样?

// 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
}

【讨论】:

    猜你喜欢
    • 2016-12-16
    • 1970-01-01
    • 2017-10-17
    • 2013-10-22
    • 2021-03-09
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多