tags:

views:

9329

answers:

6

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

+1  A: 

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.

Javier
Ouch! I guess I am going to be coding server side then - I refuse to use regex's
That's a ridiculous thing to say and you deserve hours of hard coding pain to teach you a lesson.
adam
Hi Adam! haha - I've had to use them more then often to realize to avoid them wherever possible ;-)
Uh.... refuse... to... use... regexs??? What kind of a programmer are you ;)
SoloBold
A: 

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.

Matthew Crumley
+2  A: 

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
EndangeredMassa
Thank you very much
A: 

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>
EndangeredMassa
Does this work for you?
EndangeredMassa
A: 

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+')');
Vincent Robert
This fails with the following example: {foo:'sometext', bar:'more}{text'}{moo:'another string'} Because it would insert a comma into a string.
EndangeredMassa
You said in the question that you tried to split on }{, so I assumed that you do not have }{ in your values...
Vincent Robert
A: 

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);
});