Как разобрать бесконечный массив JSON из стандартного ввода в го?

Я пытаюсь написать небольшую замену i3status, небольшой программе, которая взаимодействует с i3bar, соответствующим этому протоколу. Они обмениваются сообщениями через 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 библиотека, потому что это не vaild объект json.

2 ответа

Решение

Напишите пользовательскую функцию чтения (или декодер), которая выполняет "синтаксический анализ потокового массива" следующим образом:

  1. Читать и отбрасывать ведущие пробелы.
  2. Если следующий символ не является [ затем вернуть ошибку (не может быть массивом).
  3. Пока верно делаю
    1. Вызов json.Decoder.Decode в "следующий" пункт.
    2. Уступить или обработать "следующий" предмет.
    3. Читать и отбрасывать пробелы.
    4. Если следующий символ:
      1. Запятая , затем продолжите цикл for в #3.
      2. Закрывающая скобка ] затем выйдите из цикла for в #3.
      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
}

То, что вы ищете, является потоковым API для JSON. Есть много доступных, быстрый поиск Google показал этот проект, который перечисляет Streaming как одну из его продвинутых функций.

Другие вопросы по тегам