Я получаю данные из 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, чтобы можно было их проанализировать, но не знаю, как это сделать. Как я могу этого добиться?
действительно ли это так? нет [
в начале? является ли каждый фрагмент самостоятельной действительной строкой JSON? или они ,
действительно в стриме?
Они не разделены запятыми, они выглядят как строки json, за которыми следует другая, как я показал. Я не знаю, как они разграничены, так как приходят из другого приложения. Единственная информация, которую я знаю, это то, что он отправляет поток данных. Я изменил вывод, удалив запятые
Отредактировано
Для обработки потоковых данных 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 станут доступными, я хочу проанализировать их как объекты json и добавить в фрагменты, чтобы можно было отображать их в фрагментах данных json, иначе у меня закончится память. Я также получаю сообщение об ошибке SyntaxError: Ожидаемое имя свойства или «}» в JSON в позиции 1 (строка 1, столбец 2).
Извините за путаницу, я обновил решение для эффективной обработки и анализа данных 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
.
Строки составляют около 100 на фрагмент, возвращаемый read(), поэтому я хочу обработать их, добавив 100 объектов json. Вышеупомянутое на самом деле не дало мне ожидаемого количества объектов json, поскольку в большинстве случаев было \n.
Если разделение не сработало, приведите пример того, где оно пошло не так. Разрыв строки в строке JSON никогда не может быть разделенной позицией, если только он не находится между }
и {
.
Если они всегда разделены запятыми, можно ли обернуть строку в символы
[]
... тогда вы могли бы просто запуститьJSON.parse
?