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