Как читать поток JSON-объектов на объект

У меня есть двоичное приложение, которое генерирует непрерывный поток объектов JSON (не массив объектов JSON). Иногда объект Json может занимать несколько строк (все еще являясь допустимым объектом JSON, но предварительно проверенным).

Я могу подключиться к этому потоку и читать его без проблем, как:

var child = require('child_process').spawn('binary', ['arg','arg']);

child.stdout.on('data', data => {
  console.log(data);
});

Потоки являются буферами и генерируют события данных, когда им заблагорассудится, поэтому я поиграл с модулем readline, чтобы разобрать буферы в строки, и это работает (я могу JSON.parse() строка) для объектов Json, которые не охватывают на нескольких строках.

Оптимальным решением будет прослушивание событий, которые возвращают один объект json, что-то вроде:

child.on('json', object => {

});

Я заметил опцию objectMode в документации по узлам потоков, однако я получаю поток в формате буфера, поэтому я считаю, что не могу его использовать.

Посмотрел npm на pixl-json-stream, json-stream, но, по моему мнению, ни один из них не подходит для этой цели. Существует кларнет-объект-поток, но для этого потребуется построить объект json с нуля на основе событий.

Я не контролирую поток объектов json, большую часть времени один объект находится на одной строке, однако 10-20% времени объект json находится на нескольких строках (\n как EOL) без разделителя между объектами. Каждый новый объект всегда начинается с новой строки.

Образец потока:

{ "a": "a", "b":"b" }
{ "a": "x",
  "b": "y", "c": "z"
}
{ "a": "a", "b":"b" }

Должно быть, решение уже есть, я просто упускаю что-то очевидное. Лучше бы найти соответствующий модуль, чтобы затем взломать с помощью regexp анализатор потока для обработки этого сценария.

2 ответа

Решение

Я бы порекомендовал попробовать разбирать каждую строку:

const readline = require('readline');

const rl = readline.createInterface({
 input: child.stdout
});

var tmp = ''
rl.on('line', function(line) {
  tmp += line
  try {
    var obj = JSON.parse(tmp)
    child.emit('json', obj)
    tmp = ''
  } catch(_) {
    // JSON.parse may fail if JSON is not complete yet
  }
})

child.on('json', function(obj) {
  console.log(obj)
})

Поскольку дочерний элемент является EventEmitter, можно просто вызвать child.emit('json', obj).

Имея такое же требование, мне было неудобно применять требование для новых строк для поддержки readline, мне нужно было уметь обрабатывать начало чтения в середине потока (возможно, в середине документа JSON), и мне не нравилось постоянно анализировать и проверка на ошибки (показалась неэффективной).

Поэтому я предпочел использовать clarinet синтаксический анализатор, собирая документы, как я пошел и испуская doc события, когда целые документы JSON были проанализированы.

Я только что опубликовал этот класс в NPM

https://www.npmjs.com/package/json-doc-stream

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