Due to the way my serverside script outputs I receive multiple JSON objects. {jsonhere}{jsonhere1}{jsonhere2}{jsonhere3} etc.. They aren't seperated by anything. If I would do a split based }{ I would lose those brackets. So is there an outerloop I can put over the regular $.each loop to make this work?
Thank you,
Ice
6 Answers 6
Rough algorithm:
Define a stack
Define an array
LOOP on each character in the string
IF the top item of the stack is a single or double quote THEN
LOOP through each character until you find a matching single or double quote, then pop it from the stack.
ELSE
IF "{", push onto the stack
IF "}" THEN
pop a "{" from the stack if it is on top
IF the stack is empty THEN //we just finished a full json object
Throw this json object into an array for later consumption
END IF
END IF
IF single-quote, push onto the stack
IF double-quote, push onto the stack
END IF
END LOOP
Comments
this isn't JSON.
jQuery interprets JSON the lazy way, calling eval() and hoping there's no 'real' code in there; therefore it won't accept that. (i guess you already know this).
it seems your only way out is to treat it as a text string, and use regular expressions to extract the data.
3 Comments
The following would be a valid JSON response:
[
{JSON},
{JSON},
{JSON},
{JSON}
]
Since your JSON is malformed, simply fix it on the server side. The following code is a bit more brief than what EndangeredMassa suggested and it avoids adding a comma in between braces enclosed in quotes. I'm not so good at RegEx to figure it out with a single .replace().
var string = "{\"key\":\"val}{ue\"}{'key':'val}{ue'}{ \"asdf\" : 500 }";
var result = string.match(/('.*?')|(".*?")|(\d+)|({)|(:)|(})/g);
var newstring = "";
for (var i in result) {
var next = parseInt(i) + 1;
if (next <= result.length) {
if (result[i] == "}" && result[next] == "{") {
newstring += "},";
}
else {
newstring += result[i];
}
}
}
To loop through the JSON objects use the following:
$.each(eval(newstring), function() {
//code that uses the JSON values
alert(this.value1);
alert(this.value2);
});
Comments
Unless you can guarantee that any strings in the data will not contain "}{", you can't even safely split it without parsing the JSON at least enough to keep track of whether you are in a string or not. For example if you just split this:
{"foo": "}{", "bar": 42}
around "}{", you would end get two invalid JSON objects instead of one.
If you know that can never happen, you could split around the braces and append "}" to each element except the last one, and prepend "{" to the last element. I would only do that if there was absolutely no other way though, because it's really fragile.
Comments
I managed to cobble together a working example! Save the following text as an html page. It appears to work well in IE7 and FF3. Let me know if you have any trouble with it.
(I used jquery, but it's really not necessary at all.)
<html>
<head>
<script type="text/javascript" src="jquery-1.2.6.js"></script>
<script type="text/javascript">
function handleClick() {
var jsonStrs = parse();
var jsonObjs = [];
for(var j=0;j<jsonStrs.length;j++) jsonObjs.push( parseJSON(jsonStrs[j]) );
//jsonObjs now contains an array of json objects
document.getElementById('log').innerHTML = '';
displayResults(jsonObjs);
}
function displayResults(jsonObjs) {
for(var k=0; k<jsonObjs.length; k++) {
ShowObjProperties(jsonObjs[k]);
}
}
function ShowObjProperties(obj) {
var property, propCollection = "";
for(property in obj) {
propCollection += (property + ": " + obj[property] + "<br>");
}
log(propCollection);
}
function parseJSON(str) {
var x_result = null;
eval('x_result = ' + str);
return x_result;
}
function parse() {
//Setup
var out = $('#output');
var rawinput = $('#inputtext').val();
var input = rawinput.split('');
var stack = [];
stack.top = function() {
if (this.length == 0) return null;
return this[this.length-1];
}
var jsonStrs = [];
//Main Loop
var ch = '';
var top = '';
var cursor = 0;
var i = 0;
while (i<input.length) {
//Current Character
ch = input[i];
top = stack.top();
if(top == "'" || top == '"') { //Ignore the rest of the string
//You can add validation for possible unsafe javascript inside a string, here.
ch = input[++i];
while(ch != top) {
i++;
if(i>=input.length) {
alert('malformed string');
break;
}
ch = input[i];
}
stack.pop();
} else {
//You can add validation for unsafe javascript here.
if(ch == ' ') {
i++;
continue; // Ignore spaces
}
if(ch == "{" || ch == "'" || ch == '"') stack.push(ch);
if(ch == "}") {
if(top=="{") {
stack.pop();
} else {
alert('malformed string');
break;
}
if(stack.length == 0) {
var str = rawinput.substring(cursor, i+1)
jsonStrs.push(str);
cursor = i+1;
}
}
}
i++;
}
return jsonStrs;
}
function log(msg) {
document.getElementById('log').innerHTML += msg + '<br>';
}
</script>
</head>
<body>
<textarea id="inputtext" rows="5" cols="40" style="overflow:auto">{foo:'bar'}</textarea><br>
<button id="btnParse" onclick="handleClick();">Parse!</button><br /><br />
<div id="output">
</div>
<b>Results:</b>
<div id="log"></div>
</body>
</html>
Comments
This is not JSON, on the client side just fix your JSON code and you can 'eval' it safely.
var jsonWrong = '{a:1}{a:2}{a:3}';
var jsonRight = jsonWrong.replace('}{', '},{');
var json = eval('('+jsonRight+')');