データベースから複数のJSONオブジェクトをストリーミングで返すSinatraサーバーがあります。オブジェクトは次のようになります。
{"a": 1, "b": 2, "c": 3}
{"a": 4, "b": 5, "c": 6}
...
しかし、これは無効なJSONです。 Sinatraのイベント処理にハックを追加して(不足している配列区切り文字を手動で挿入)、応答を次のようにします。
[
{"a": 1, "b": 2, "c": 3}
, {"a": 4, "b": 5, "c": 6}
]
これは有効なJSONですが、この手法は洗練されていません。このクライアント側を行う方法はありますか?基本的には、JavaScript関数で文字列を読み取って有効なJSONオブジェクトを消費し、JSONオブジェクトと残りの文字列を返して、文字列全体が消費されるまで繰り返し呼び出します。
ネイティブのJSON.parse()
関数は、文字列全体が有効なJSONであることを期待します。私は最初の有効なオブジェクトのみを消費するパーサーを気にしていません。そして人々はとにかく本当に有効なJSONを生成しているはずです。
1行に1つのオブジェクトがあることがわかっている場合は、split()
関数を使用して文字列を行ごとに分割し、各行を個別に解析できます。
var str = '{"a": 1, "b": 2, "c": 3}\n'+
'{"a": 4, "b": 5, "c": 6}';
var strLines = str.split("\n");
for (var i in strLines) {
var obj = JSON.parse(strLines[i]);
console.log(obj.a);
}
文字列操作を少し使用して、各行を配列要素に変換し、全体を解析することもできます。
str = "["+str.replace(/\n/g, ",")+"]";
JSON.parse(str);
私はこれを行います:
var str = '{"a": 1, "b": 2, "c": 3}{"a": 4, "b": 5, "c": 6}';
var res = JSON.parse('[' + str.replace(/}{/g, '},{') + ']');
編集:
trembyのコメントに対する回答として
var str = '{"a": 1, "b": 2, "c": 3}{"a": 4, "b": 5, "c": 6}';
var res = JSON.parse('[' + str.replace(/}{(?=([^"]*"[^"]*")*[^"]*$)/g, '},{') + ']');
JSON文字列が1行の場合、次のようなことができます。
_var splitPoint = remainingData.indexOf("\n");
var currentJSONStr = splitPoint > -1 ? remainingData.substr(0, splitPoint) : remainingData;
remainingData = splitPoint > -1 ? remainingData.substr(splitPoint+1) : '';
var dataObj = youJSONDecodeFuncOrEval(currentJSONStr);
_
そうでない場合は、私の答えを無視してください。
これがお役に立てば幸いです。
アリン
注:私は要件を満たそうとしました
基本的には、JavaScript関数で文字列を読み取って有効なJSONオブジェクトを消費し、JSONオブジェクトと残りの文字列を返して、文字列全体が消費されるまで繰り返し呼び出します。
これが.split("\n")
を使用しなかった理由です。
o.stringはjsonオブジェクトです。
オブジェクトの配列または複数のjsonオブジェクトに「new」などの文字列を追加します。
たとえば:
json object----
{"id":2,"method":"listWirings","params":{"language":"anonymousLanguage","name":"mytest","working":"{\"modules\":[{\"config\":{\"position\":[186,59],\"xtype\":\"WireIt.ImageContainer\"},\"name\":\"Start\",\"value\":{}},{\"config\":{\"position\":[188,265],\"xtype\":\"WireIt.ImageContainer\"},\"name\":\"Stop\",\"value\":{}}],\"properties\":{\"description\":\"gfd\",\"name\":\"gf\"},\"wires\":[{\"src\":{\"moduleId\":0,\"terminal\":\"_OUTPUT\"},\"tgt\":{\"moduleId\":1,\"terminal\":\"StpIn\"}}]}"},"version":"json-rpc-2.0"}new
var str = o.toString();
var s = str.split("new");
for (var i = 0; i < s.length-1; i++)
{
var r = YAHOO.lang.JSON.parse(s[i]);
}
これが複数のjsonオブジェクトを解析することを願っています。
これは最も効率的ではないかもしれませんが、仕事を完了する必要があります。
var s = '{"a": 1, "b": 2, "c": 3}{"a": 4, "b": 5, "c": 6}';
var sTemp = "";
var aObjs = [];
for(var i=0; i<s.length; ++i)
{
sTemp += s[i];
if (s[i] == "}")
{
aObjs.Push(JSON.parse(sTemp));
sTemp = "";
}
}
オブジェクト間に改行文字があることがわかっている場合は、はるかに簡単になります。
var sBad = '{"a": 1, "b": 2, "c": 3}\n{"a": 4, "b": 5, "c": 6}';
var sGood = "[" + sBad.replace(/\n/g, ",") + "]";
var aObjs = JSON.parse(sGood);
この小さなJavaScript関数を作成しました。これにより、任意の文字列をJsonオブジェクトに解析できます。各キャラクターを通過し、階層をメモすることで機能します。このソリューションの利点は、何がそれらを分離しているかを知らなくても、テキストのすべてのJsonオブジェクトを取得できることです。
function evaluateJsonString(string){
var start = string.indexOf('{');
if(start == -1)
return false;
let hierarchy = 0;
let characters = string.split('');
let objects = [];
for(var index = start; index < characters.length; index++){
let char = characters[index];
if(char == '{')
hierarchy++;
if(char == '}')
hierarchy--;
if(hierarchy == 0){
objects.Push(JSON.parse(characters.slice(start, index + 1).join('')));
index = start = index + characters.slice(index, characters.length).indexOf('{') - 1;
if(start == -1)
break;
}
}
return objects;
}
let result = evaluateJsonString('This is {"name": "John", "age": 32, "hobbies": ["sport", "programming"]} He goes to {"name": "University", "director": {"name": "Peter", "age": 66, "hobbies": ["drinking coffee"]}}');
console.log(result);
今日、これを行うための小さなモジュールを作成し、NPMに json-multi-parse
として公開しました。 コードはGithubで入手可能 。
私の解決策は単純ですが、そのような文字列を解析するときにJSON.parse
がスローするエラーメッセージに依存しているため、確かに壊れやすい可能性があります。エラーで指定された位置番号(「予期しないトークン{JSONの位置xyz内の番号」)を使用してそれまでのすべてを解析し、その後再帰してすべてを解析します。
ただし、ここにある他の提案ソリューションの一部がそうであるように、文字列の中括弧が原因で壊れることはありません。
これは、ChromeおよびNodeで機能するコードの単純なバージョンです。
const ERROR_REGEX = /^Unexpected token { in JSON at position (\d+)$/;
function jsonMultiParse(input, acc = []) {
if (input.trim().length === 0) {
return acc;
}
try {
acc.Push(JSON.parse(input));
return acc;
} catch (error) {
const match = error.message.match(ERROR_REGEX);
if (!match) {
throw error;
}
const index = parseInt(match[1], 10);
acc.Push(JSON.parse(input.substr(0, index)));
return jsonMultiParse(input.substr(index), acc);
}
}
Firefoxもサポートしたい場合はさらに複雑になり、行番号とその行内の文字を示す形式でエラーが発生します。上でリンクしたモジュールがこのケースを処理します。
データストリームが1行で複数のJSONオブジェクトを提供している場合は、それらを配列に分割する必要があります。
const str = '{"a": 1, "b": 2, "c": 3}\n' +
'{"a": 4, "b": 5, "c": 6}' +
'{"a": 7, "b": 8, "c": 9}';
const json = '[' + str.replace(/}\n?{/g, '},{') + ']';
JSON.parse(json).forEach((obj) => {
console.log('a:', obj.a);
});