web-dev-qa-db-ja.com

複数のJSONオブジェクトの送信/解析

データベースから複数の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オブジェクトと残りの文字列を返して、文字列全体が消費されるまで繰り返し呼び出します。

18
Kenny Peng

ネイティブの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);
14
Alex Jasmin

私はこれを行います:

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, '},{') + ']');
2
Chris

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")を使用しなかった理由です。

2
Alin Purcaru

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オブジェクトを解析することを願っています。

1
Kishore Ganti

これは最も効率的ではないかもしれませんが、仕事を完了する必要があります。

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);
1
roto

この小さな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);
1
LuSt

今日、これを行うための小さなモジュールを作成し、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
tremby

データストリームが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);
});
0
YodasWs