Comment puis-je analyser le premier objet JSON sur un flux dans JS

J'ai un flux d'objets JSON, comme avec JSON-RPC sur TCP ou WebSockets. Il n'y a pas de préfixe de longueur ou de délimiteur, car JSON est auto-délimitant. Donc, quand je lis le flux, je peux finir avec quelque chose comme ça:

{"id":1,"result":{"answer":23},"error":null} {"id":2,"result":{"answer":42},"error":null} {"id":3,"result":{"answ 

Je dois analyser chaque objet JSON un par un. Je ne peux pas le faire avec JSON.parse, car il va simplement lancer une erreur de syntaxe pour les données étrangères à la fin.

Bien sûr, avec cet exemple, je pourrais aller ligne par ligne, mais je ne peux pas compter sur les espaces blancs comme ça; JSON-RPC peut aussi facilement ressembler à ceci:

 { "id": 1, "result": { "answer": 23 }, "error":null } 

Ou ca:

 {"id":1,"result":{"answer":23},"error":null}{"id":2,"result":{"answer":42},"error":null} 

Avec la plupart des analyseurs dans d'autres langues, la réponse évidente est quelque chose comme ceci (en utilisant Python comme exemple):

 buf = '' decoder = json.JSONDecoder() def onReadReady(sock): buf += sock.read() obj, index = decoder.raw_decode(buf) buf = buf[index:] if obj: dispatch(obj) 

Mais je ne trouve rien de semblable dans JS. J'ai regardé tous les analyseurs JS que je peux trouver, et tous sont effectivement équivalents à JSON.parse.

J'ai essayé de regarder différents cadres JSON-RPC pour voir comment ils traitent ce problème, et ils ne le font pas. Beaucoup d'entre eux supposent qu'un recv retournera toujours une seule envoi (ce qui fonctionne bien pour JSON-RPC sur HTTP, mais pas sur TCP ou WebSockets, bien qu'il puisse sembler fonctionner dans des tests locaux, bien sûr). D'autres ne traitent pas directement JSON-RPC car ils ajoutent des exigences sur les espaces (dont certains ne sont même pas valables pour JSON-RPC).

Je pourrais écrire un contrôle du délimiteur qui équilibre les parenthèses et les guillemets (manipuler l'échappement et le cotation, bien sûr), ou simplement écrire un analyseur JSON à partir de zéro (ou le port d'un autre langue, ou modifier http://code.google.com/p / Json-sans-eval / ), mais je ne peux pas croire que personne ne l'ait déjà fait.

EDIT: J'ai fait deux versions moi-même, http://pastebin.com/fqjKYiLw basé sur json-sans-eval et http://pastebin.com/8H4QT82b basé sur l'analyseur de descendance récursive de référence Crockford json_parse.js. Je préférerais toujours utiliser quelque chose qui a été testé et utilisé par d'autres personnes plutôt que de le coder moi-même, alors je laisse cette question ouverte.

Après un mois de recherche d'alternatives et de ne pas avoir trouvé d'utilité, j'ai décidé de coder une série d'implémentations différentes et de les tester, et je suis allé avec ma modification de l'analyseur de descendance récursive de référence de Crockford (comme décrit dans la question, disponible ici ).

Ce n'était pas le plus rapide, mais c'était plus que suffisant dans tous les tests que je faisais. Plus important encore, il rattrape clairement JSON erroné, alors que ce n'est pas ambigu avec JSON incomplète, beaucoup mieux que la plupart des autres alternatives. Plus important encore, il a fallu très peu, et très simple, des changements à partir d'une base de code bien connue et testée, ce qui me rend plus confiant dans sa justesse.

Pourtant, si quelqu'un sait une meilleure bibliothèque que la mienne (et tout simplement être utilisé par beaucoup de projets au lieu de que seul je compte comme une qualification majeure), j'aimerais en savoir plus.

Voici un simple séparateur d'objets JSON. Il suppose que vous recevez une série d'objets JSON (pas de tableau) et qui sont bien formés.

 function JSONObjectSepaator() { this.onObject = function (JSONStr) {}; this.reset = function () { this.brace_count = 0; this.inString = false; this.escaped = false; this.buffer = ""; }; this.receive = function (S) { var i; var pos=0; for (i = 0; i < S.length; i++) { var c = S[i]; if (this.inString) { if (this.escaped) { this.escaped = false; } else { if (c == "\\") { this.escaped = true; } else if (c == "\"") { this.inString = false; } } } else { if (c == "{") { this.brace_count++; } else if (c == "}") { this.brace_count--; if (this.brace_count === 0) { this.buffer += S.substring(pos,i+1); this.onObject(this.buffer); this.buffer = ""; pos=i+1; } } else if (c == "\"") { this.inString = true; } } } this.buffer += S.substring(pos); }; this.reset(); return this; } 

Pour l'utiliser, vous pouvez le faire de cette façon:

 var separator = new JSONObjectSepaator(); separator.onObject = function (o) { alert("Object received: "+o); }; separator.receive('{"id":1,"result":{"answer":23},"error":null, "x'); separator.receive('x":"\\\""}{"id":2,"result":{"answer":42},"error":null}{"id":'); separator.receive('3,"result":{"answer":43},"err{or":3}');