Я получаю данные из API, который отправляет данные в потоке. К сожалению, когда я обрабатываю данные порциями, эти порции представляют собой несколько строк JSON, подобных этой;
{
"productName": "bag",
"code": "BGX-112"
}
{
"productName": "purse",
"code": "PUSR-112"
}
etc..
Вот код, который обрабатывает данные:
const getProductData = async (url:string) => {
const decoder = new TextDecoderStream()
let chunks: any[];
chunks = [];
try {
const response = await fetch(url)
const stream = response?.body.pipeThrough(new TextDecoderStream());
const reader = stream.getReader()
while (true) {
const {value, done} = await reader.read();
if (done) {
// Flush any buffered characters.
const stringArray = value?.split(/\b\s/);
chunks.push(value);
return chunks;
}
if (value) {
const stringArray = value?.split(/\b\s/);
chunks.push(value);
}
}
}catch (e) {
console.error("Error ",e)
}
}
Я хочу получить фрагменты как отдельные объекты JSON, чтобы можно было их проанализировать, но не знаю, как это сделать. Как я могу этого добиться?
🤔 А знаете ли вы, что...
JavaScript позволяет создавать собственные серверные приложения с использованием платформы Node.js.
Отредактировано
Для обработки потоковых данных JSON используйте TextDecoder для объединения фрагментов в строку. Найдите разделители (например, }\n{), чтобы идентифицировать полные объекты JSON. Разделите строку в этих точках и проанализируйте каждый сегмент с помощью JSON.parse()
.
Серверная сторона:
const express = require('express');
const app = express();
const port = 3000;
app.get('/large-json', (req, res) => {
res.setHeader('Content-Type', 'application/json');
for (let i = 0; i < 1000; i++) {
const jsonObject = JSON.stringify({ productName: `item${i}`, code: `CODE-${i}` });
res.write(jsonObject + '\n'); // Separate objects with a newline
}
res.end();
});
app.listen(port, () => {
console.info(`Server running at http://localhost:${port}/`);
});
Клиентская сторона:
const getProductData = async (url: string) => {
try {
const response = await fetch(url);
const reader = response.body?.getReader();
let decoder = new TextDecoder();
let partialData = '';
if (!reader) throw new Error("Stream reader not available");
while (true) {
const { value, done } = await reader.read();
if (done) break;
partialData += decoder.decode(value, { stream: true });
let lines = partialData.split('\n');
partialData = lines.pop()||'';
for (let line of lines) {
if (line.trim()) {
try {
const jsonObject = JSON.parse(line);
console.info(jsonObject);
} catch (e) {
console.error("JSON parsing error: ", e);
}
}
}
}
if (partialData.trim()) {
try {
const jsonObject = JSON.parse(partialData);
console.info(jsonObject);
} catch (e) {
console.error("JSON parsing error in final chunk: ", e);
}
}
} catch (e) {
console.error("Error ", e);
}
};
getProductData("http://localhost:3000/large-json");
Даже если приложение отправляет фрагменты, каждый из которых состоит ровно из одной строки JSON, stream
, выходящий из текстового декодера, может объединить несколько отправленных фрагментов в один фрагмент, видимый reader
.
Если бы строки JSON были огромными, могло бы случиться так, что даже одна из них была бы разделена на два фрагмента, видимых reader
. В приведенном ниже решении предполагается, что это не так (другими словами: приложение передает много маленьких строк JSON, а не одну огромную).
При таком предположении код ниже разбивает каждый фрагмент, видимый reader
, в тех позициях, где за закрывающей скобкой следует (пробел и) открывающая скобка. Этого достаточно, если каждая строка JSON представляет собой просто объект productName
/code
, как указано в вашем вопросе. Если строки JSON более сложные, возможно, потребуется адаптировать разделение.
(async function() {
const reader = (await fetch("https://httpbin.org/stream/10")).body.pipeThrough(new TextDecoderStream()).getReader();
while (true) {
const {
value,
done
} = await reader.read();
console.info("new chunk read, done = ", done);
for (const v of value.split(/(?<=\})\s*(?=\{)/))
console.info(JSON.parse(v).id);
if (done) return;
}
})();
10 фрагментов JSON, отправленных приложением, образуют 2 фрагмента (плюс пустой), видимые reader
.